使用Scala + sbt + sbt-android-plugin开发Android应用程序

开发Android程序通常所使用的语言是Java,但Scala做为一种有希望替代Java的语言,也逐渐被越来越多的Android者所熟悉。本文简要的介绍如何使用Scalasbt工具(Simple Build Tool)及sbt-android-plugin创建,编译,安装运行一个简单的Android程序。

  1. 环境准备

    Easwy的开发环境搭建在Ubuntu 10.04.3上,所以第一步是安装Ubuntu,具体过程略过。

    然后需要下载Android SDK,这一步可以参考文档Download the Android SDK完成。

    接下来在Ubuntu里安装Sun Java JDK,如果你使用OpenJDK,可以跳过这一步。首先去掉/etc/apt/sources.list中这两行前的注释,使能Java 6源:

    $ sudo vim /etc/apt/sources.list
    deb http://archive.canonical.com/ubuntu lucid partner
    deb-src http://archive.canonical.com/ubuntu lucid partner 

    然后安装Java 6 JDK:

    $ sudo aptitude install sun-java6-jdk 
  2. 安装Scala、sbt和sbt-android-plugin

    首先安装Scala。到Scala Download Page下载Scala,Easwy下载的是Scala 2.9.1的IzPack Installer,下载后执行:

    $ java -jar scala-2.9.1.final-installer.jar 

    按提示安装即可。

    然后安装sbt。到sbt wiki上下载sbt-launch.jar,然后创建一个脚本来调用它:

    $ vim ~/bin/sbt
    java -Xms512M -Xmx1536M -Xss1M -XX:+CMSClassUnloadingEnabled -XX:MaxPermSize=384M -jar `dirname $0`/sbt-launch.jar "$@"
    
    $ chmod u+x ~/bin/sbt 

    最后安装sbt-android-plugin。因为sbt-android-plugin还在开发中,在此Easwy直接checkout它的源代码,然后安装在本地:

    $ git clone git://github.com/jberkel/android-plugin.git
    $ cd android-plugin
    $ sbt publish-local 

    sbt在第一次运行时会下载它所依赖的包,耗时会久一些,此过程只需进行一次。

  3. 自动生成Android应用程序框架

    设置好上面的环境后,可以使用sbt-android-plugin来自动生成一个简单的Android应用程序框架。插件的作者推荐使用一个名为giter8的模板工具来生成框架。首先需要下载giter8工具:

    $ cd ~/bin
    $ curl https://raw.github.com/n8han/conscript/master/setup.sh | sh
    $ ~/bin/cs n8han/giter8 

    这几条命令会下载一些它们所需的包,需要一些时间。接下来就可以用giter8工具,根据指定的模板自动生成一个Android应用程序:

    $ ~/bin/g8 jberkel/android-app
    
    Template for Android apps in Scala
    
    package [my.android.project]: com.easwy.projects.helloworld
    name [My Android Project]: Hello World
    main_activity [MainActivity]:
    scala_version [2.9.1]:
    api_level [10]:
    useProguard [true]:
    
    Applied jberkel/android-app.g8 in hello-world 

    这条命令会提示你输入一些参数,然后根据你输入的参数生成一个Android应用程序的框架。

    事实上,此命令直接找到保存在github.com上的模板,然后根据模板生成Android应用程序的目录框架。在本例中所使用的模板是jberkel/android-app。你也可以自己创建一套模板,生成更适合自己的目录框架。

  4. 编译、安装、运行Android程序

    由上面的模板生成的Android应用程序,其实就是一个简单的“Hello World!”应用,你可以直接编译、安装、运行它。

    首先进入sbt的交互模式:

    $ cd hello-world
    $ export ANDROID_SDK_ROOT=~/bin/android-sdk-linux_86
    $ sbt

    开始编译:

    > android:package-debug 

    然后用下面的命令启动你的Android模拟器(如果你还没有模拟器,参考Managing Virtual Devices创建一个):

    > android:emulator-start api10

    上面的命令启动了名为api10的模拟器,下面安装运行我们编译出来的Hello World程序:

     > android:start-emulator 

    现在可以看到Hello World程序已经在api10 emulator中运行了。

在后续的文章中,Easwy会继续介绍使用Scala开发Android程序的心得。

更多内容,请阅读易水博客上的其它文章。

[ 参考文档 ]

原创文章,请阅读页脚的许可方式,转载请注明:转载自易水博客 [ http://easwy.com/blog/ ]

本文链接地址: http://easwy.com/blog/archives/using-scala-sbt-for-android-development/

文章的脚注信息由WordPress的wp-posturl插件自动生成

在scala中判断一个对象是否是元组(Tuple)

在scala user邮件列表中看到一个函数,判断一个对象是否是元组。从这个函数中,Easwy才了解到原来模式匹配(Pattern Match)也可以应用于正则表达式,这使得在scala中使用正则表达式(regex)更加容易了。另外,这个函数还展现了scala的另外两个特点:

  1. 尽量使用递归解决方案,而不是使用循环。这样做的优点之一是避免使用变量,优点之二是代码简洁。是否有其它优点,Easwy仍在总结。但Easwy始终有个担心,递归会不会导致效率降低?会不会有堆栈溢出风险?
  2. 使用Option类型做为函数返回值。使用Option类型的好处很明显,这样你的函数既可以返回执行失败的情况(None),也可以在执行成功时给出有用的返回值。这比使用tru/false作为返回值方便很多。

函数主体如下,为了方便讲解,在前面加了行号:

    
1 val Ptrn = """scala.Tuple(\d+)""".r
2
3 def checka( x: Class[ _ ]) : Option[ Int ] = x.getName match {
4      case Ptrn( i ) => Some( i.toInt )
5      case _ => { val sc = x.getSuperclass; if( sc != null ) checka( sc ) else None }
6 }
7
8 def isTuple( x: AnyRef ) = if( x.isInstanceOf[ Product ]) checka( x.getClass ) else None
     
  1. 行1定义了一个Pattern对象,可以看到,在scala中使用正则表达式非常的简单。
  2. 行3定义了一个递归函数checka(),它的参数是Class[_],返回值是Option[Int]类型。参数”Class[_]”的意思是这个函数可以接受任意Class[A]类型,也就是接受任何参数化的Class类型(Type Parameterization)。

  3. 从行3的后半句,到行5,是一个模式匹配,检查类的名字是否匹配正则表达式Ptrn。如果类名匹配Ptrn,也就是说是一个Tuple,则返回它的维数。例如,对Tuple3返回Some(3)。如果类名不匹配Ptrn,递归调用checka()检查其父类是否为Tuple,如果全部失败,则返回None。
  4. 行8定义isTuple()函数,调用checka()判断是否为Tuple。它首先会检查x是否是一个Product实例,满足时才调用checka(),否则直接返回None。

Easwy感觉checka()函数中的递归写的不是很好,在看过”The Little Schemer”后,Easwy更倾向与下面的写法:


    
  def checka(x: Class[_]): Option[Int] = x match {
    case null => None
    case _ => x.getName match {
      case Ptrn(i) => Some(i.toInt)
      case _ => checka(x.getSuperclass)
    }
  }
     

下面是Easwy用来测试该函数的程序,全文如下:


    
object TestTuple {
  def main(args: Array[String]) {
    class ttt(a: Any, b: Any, c: Any) extends Tuple3(a, b, c)

    val test = List(new Tuple2(1, 2), new ttt(1, 2, 3), "Hello World")

    for (elem <- test)
      isTuple(elem) match {
        case None => println("Not Tuple")
        case Some(x) => println("Is Tuple" + x)
      }
  }

  val Ptrn = """scala.Tuple(\d+)""".r

  def checka(x: Class[_]): Option[Int] = x match {
    case null => None
    case _ => x.getName match {
      case Ptrn(i) => Some(i.toInt)
      case _ => checka(x.getSuperclass)
    }
  }

  def isTuple( x: AnyRef ) = if( x.isInstanceOf[ Product ]) checka( x.getClass ) else None
}
    
  

有兴趣的朋友可以编译运行一下,体会一下scala的简捷与优雅。

更多内容,请阅读易水博客上的其它文章。

[ 参考文档 ]

  • [scala-user] Trying to discover if a object is a tuple

原创文章,请阅读页脚的许可方式,转载请注明:转载自易水博客 [ http://easwy.com/blog/ ]

本文链接地址: http://easwy.com/blog/archives/test_if_a_object_is_a_tuple_in_scala/

文章的脚注信息由WordPress的wp-posturl插件自动生成

scala学习:添加行号

我的第一个scala脚本完成的功能就是为文本文件添加行号,不过那个脚本中使用了变量,不太符合scala编程的思想。现在把这个脚本改造了一下,去掉var变量的使用,完全使用val值。另外,把脚本改成了需编译的程序。

LineNumber.scala程序如下:

import scala.io.Source

object LineNumber {
  def main(args: Array[String]) {
    for (file <- args) {
      println("File: " + file)
      println("-" * 32)
      for (line <- addLineNumber(file))
        print(line)
    }
  }

  def addLineNumber(fileName: String): List[String] = {
    def padding(m: Int, i: Int) = " " * (m - i.toString.length)

    val lines = Source.fromFile(fileName).getLines.toList
    val maxWidth = lines.length.toString.length

    for {
      (l, i) <- lines.zipWithIndex
      j = i + 1
      line = padding(maxWidth, j) + j + " " + l
    } yield line
  }
} 

这个程序会从命令行获取要添加行号的文件名,把加了行号的文本打印到屏幕上。

用下面的命令编译该程序:

    fsc LineNumber.scala 

然后可以用下面的命令执行该程序,输出如下:

$ scala -cp . LineNumber LineNumber.scala
File: LineNumber.scala
--------------------------------
 1 import scala.io.Source
 2
 3 object LineNumber {
 4   def main(args: Array[String]) {
 5     for (file <- args) {
 6       println("File: " + file)
 7       println("-" * 32)
 8       for (line <- addLineNumber(file))
 9         print(line)
10     }
11   }
12
13   def addLineNumber(fileName: String): List[String] = {
14     def padding(m: Int, i: Int) = " " * (m - i.toString.length)
15
16     val lines = Source.fromFile(fileName).getLines.toList
17     val maxWidth = lines.length.toString.length
18
19     for {
20       (l, i) <- lines.zipWithIndex
21       j = i + 1
22       line = padding(maxWidth, j) + j + " " + l
23     } yield line
24   }
25 }
  

学习scala有大半个月了,越来越感受到了scala的简洁与强大。

更多内容,请阅读易水博客上的其它文章。

原创文章,请阅读页脚的许可方式,转载请注明:转载自易水博客 [ http://easwy.com/blog/ ]

本文链接地址: http://easwy.com/blog/archives/scala-learning-add-line-number/

文章的脚注信息由WordPress的wp-posturl插件自动生成

Easwy的第一个scala脚本

正在学习scala语言,还谈不上有很深入的认识,至于为什么学习scala,最初的动力大概是它简洁的语法以及与Java的兼容性。它使用类似脚本语言的语法,实现了编译语言的执行效率,是最让Easwy心动的地方了。
看完”Programming in Scala”的前三章,参照书中的例子,我写了我的第一个scala脚本,功能很简单,它读入一个文本文件,然后把文件的内容加上行号后打印出来。
脚本程序如下:

import scala.io.Source

if (args.length > 0) {
  val lines = Source.fromFile(args(0)).getLines.toList
  val maxWidth = lines.length.toString.length
  var i = 1
  for (line <- lines) {
    val padding = maxWidth - i.toString.length
    print(" " * padding + i + " " + line)
    i += 1
  }
}
else
  println("A file name is needed")

这个脚本要使用下面的命令执行:

scala printfile.scala a-text-file-here

如果你还没有安装scala,请到scala的主页下载并安装。
2010/04/07更新
在这个脚本中使用var变量,不太符合scala的编程思想,在文章scala学习:添加行号中,Easwy将其改成符合scala方式的val形式。

原创文章,请阅读页脚的许可方式,转载请注明:转载自易水博客 [ http://easwy.com/blog/ ]

本文链接地址: http://easwy.com/blog/archives/first-scala-script-of-easwy/

文章的脚注信息由WordPress的wp-posturl插件自动生成