Eclipse,Android,Scala变得简单,但仍然无法正常工作

我最近遵循了使用Scala和Eclipse进行Android编程的方法,这样可以在不使用Proguard或Treeshake的情况下减less代码和编译时间。

在本文后面,我应该能够使用最后一个Eclipse版本(3.7),几乎是最新版本的Scala(2.8.1)在模拟器版本10上更新,在Eclipse中使用提供的插件版本2.8.3。

提出的方法是提供一个特定的ramdisk映像版本,我们可以上传scala库,这大大缩小了代码上传到仿真器的大小。

我遵循这些步骤,创build了一个hello world,添加了scala自然,添加了一个scala类,在Android Package Installer之前移动了Scala构build器,一切都很完美,但是当我从Eclipse的模拟器上启动apk时,应用程序崩溃我得到以下错误,看起来像这里 (在文档的末尾)相同:

03-29 10:29:38.505: E/AndroidRuntime(839): java.lang.NoClassDefFoundError: upg.TestSinceInstallation.ComputeSum 

如果我删除活动文件中的scala引用,它运行良好。

这里是TestSinceInstallation.java文件:

  package upg.TestSinceInstallation; import android.app.Activity; import android.os.Bundle; import upg.TestSinceInstallation.ComputeSum; public class TestSinceInstallationActivity extends Activity { /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); int a = 1; int b = 5; ComputeSum cs = new ComputeSum(a, b); if(cs.getResut() == 6) { setContentView(R.layout.main); } } } 

这里是ComputeSum.scala文件

  package upg.TestSinceInstallation class ComputeSum(a: Int, b: Int) { def getResut() : Int = a + b } 

你认为我应该怎么做才能做到这一点? 我觉得如此接近目标。

下面是解决scheme,使用Android的Eclipse 3.7和Scala 3.0.0没有任何问题。

  • 安装Eclipse 3.7 (适用于我3.7.2)和Android SDK – 如果您的系统中尚未安装Java SDK 7v22 。 注意:特殊的Android ADT Bundle不允许安装scala(截至2013年6月,linux站)
  • 通过将Eclipse指向此网站来安装适用于Eclipse的Android ADT插件版本22:

https://dl-ssl.google.com/android/eclipse/

  • 通过将Eclipse指向此网站来安装Scala IDE 3.0.0版 :

http://download.scala-ide.org/sdk/e37/scala210/stable/site

  • 将Eclipse指向此网站来安装AndroidProguardScala插件v47:

https://androidproguardscala.s3.amazonaws.com/UpdateSiteForAndroidProguardScala

现在,要创build一个scala项目,

  1. 照常创build一个Android项目
  2. 右键单击该项目, ConfigureAdd Scala nature
  3. 右键单击该项目, Add AndroidProguardScala nature

你完成了。


Scalafying android代码

现在好事发生了。 首先,你可以scalafy任何活动,你会得到访问scala独特的function,如:

  • 懒惰的评估来定义类体内的视图
  • 隐式转换函数来定制视图与代码的交互
  • 没有分号和scala的所有语法糖。
  • 使用actor来区分UI线程和处理线程。

这是其中一些例子。

 package com.example.testing; import android.app.Activity import android.os.Bundle import scala.collection.mutable.Map import android.view.View import android.widget.SeekBar import android.widget.ImageButton import android.graphics.drawable.Drawable import android.widget.TextView trait ActivityUtil extends Activity { implicit def func2OnClickListener(func: (View) => Unit):View.OnClickListener = { new View.OnClickListener() { override def onClick(v: View) = func(v) } } implicit def func2OnClickListener(code: () => Unit):View.OnClickListener = { new View.OnClickListener() { override def onClick(v: View) = code() } } private var customOnPause: () => Unit = null override def onPause() = { super.onPause() if(customOnPause != null) customOnPause() } def onPause(f: =>Unit) = { customOnPause = {() => f} } private var customOnCreate: Bundle => Unit = null override def onCreate(savedInstanceState: Bundle) { super.onCreate(savedInstanceState) if(customOnCreate != null) customOnCreate(savedInstanceState) } def onCreate(f: Bundle => Unit) = { customOnCreate = f } // Keep references alive when fetched for the first time. private implicit val vMap = Map[Int, View]() private implicit val ibMap = Map[Int, ImageButton]() private implicit val sbMap = Map[Int, SeekBar]() private implicit val tvMap = Map[Int, TextView]() private implicit val dMap = Map[Int, Drawable]() def findView[A <: View](id: Int)(implicit v: Map[Int, A]): A = v.getOrElseUpdate(id, findViewById(id).asInstanceOf[A]) def findDrawable[A <: Drawable](id: Int)(implicit v: Map[Int, A]): A = v.getOrElseUpdate(id, getResources().getDrawable(id).asInstanceOf[A]) implicit class RichView(b: View) { // Scala 2.10 ! def onClicked(f: =>Unit) = b.setOnClickListener{ () => f } } // Implicit functions to operate directly on integers generated by android. implicit def findViewImageButton(id: Int): ImageButton = findView[ImageButton](id) implicit def findViewSeekBar(id: Int): SeekBar = findView[SeekBar](id) implicit def findViewTextView(id: Int): TextView = findView[TextView](id) implicit def findDrawable(id: Int): Drawable = findDrawable[Drawable](id) implicit def findRichView(id: Int): RichView = toRichView(findView[View](id)) } 

现在所有以前的样板,编写简洁的活动是非常有用的。 请注意我们如何直接操作ID,就好像它们是视图一样。 如果可以从各种结构推断方法,则需要消除歧义作为(view: TextView).requestFocus()

 // Now after all the boilerplate, it is very useful to write consise activities class MyActivity extends Activity with ActivityUtil { import R.id._ // Contains R.id.button, R.id.button2, R.id.button3, R.id.mytextview lazy val my_button: ImageButton = button //In reality, R.id.button lazy val his_button: ImageButton = button2 onCreate { savedInstanceState => // The type is automatically inferred. setContentView(R.layout.main) my_button.setOnClickListener(myCustomReactClick _) his_button.setOnClickListener { () => //.... Scala code called after clicking his_button } button3.onClicked { // Awesome way of setting a method. Thanks Scala. } mytextview.setText("My text") // Whoaaa ! setText on an integer. (mytextview: TextView).requestFocus() // Disambiguation because the method is ambiguous // Note that because of the use of maps, the textview is not recomputed. } def myCustomReactClick(v: View) = { // .... Scala code called after clicking my_button } onPause{ // ... Custom code for onPause } } 

确保scala文件的名称与其中包含的主要活动相匹配,在这种情况下,它应该是MyActivity.scala

其次,为了build立一个scala项目作为一个图书馆项目,使用是作为一个资源不同的应用程序的基础,遵循build立一个图书馆项目的常规方式 。 右键单击想要作为基础库项目的PropertiesAndroid的scala项目,然后检查isLibrary
要使用这个库创build衍生项目,并为此生成一个APK,创build一个新的android项目,并且不添加任何scala或androidproguardscala本质,只需右键单击PropertiesAndroid ,然后将以前的scala项目添加为库。

更新使用新版本的Android插件,您应该转到Project Properties > Build Päth > Order and Export然后检查Android Private Libraries 。 这将允许在库项目和主项目中导出scala库,即使主项目没有分配Scala。

testing使用插件Robolectric可以轻松testing您的android scala项目。 只需按照创buildtesting项目的步骤,向其中添加Scala自然。 你甚至可以使用新的Scaladebugging器,并通过添加Scalatest库,你可以使用应该匹配器和许多其他functionScala。