Hatena::Groupsubtech

NaN days

ブログを移転しています。最新の記事は motemen.hatenablog.com へどうぞ

2011-01-21

Scala + sbt (simple-build-tool) で Android アプリを作るときの散漫なメモ

| 13:36 | Scala + sbt (simple-build-tool) で Android アプリを作るときの散漫なメモ - NaN days を含むブックマーク はてなブックマーク - Scala + sbt (simple-build-tool) で Android アプリを作るときの散漫なメモ - NaN days

前のエントリで紹介したアプリを作る際に、いろいろとハマったり調べたりしたので、それを書いておきます。

AndroidScala で開発するには Eclipse を使う方法や ant を使う方法などあるみたいですが、どちらもよく分からないし、長い作業の果てにほんとうに到達できるのかという疑念が湧き、うまくいかなかったのでお薦めしません。(ちなみに ant でやったバージョンが https://github.com/motemen/HelloAndroidScalaWorld にあります……)

simple-build-tool だと初めてのビルドまでの道のりが短くて楽です。sbt をインストール後 (homebrew なら brew sbt で入ります)、create_project というスクリプトを使うとプロジェクトのひな形を作ってくれます。(慣れたら自分なりのプロジェクト構成にするのがいいと思いますが)

% curl -LO https://github.com/jberkel/android-plugin/raw/master/script/create_project
% chmod +x create_project
% ./create_project PicTumblr net.tokyoenvious.droid.pictumblr --scala-version 2.8.1 --api-level 8 --activity PicTumblrActivity

とするとプロジェクトのディレクトリが作成されるので、ディレクトリの中に入って sbt を起動します。

% cd PicTumblr
% sbt

初回起動時にいろいろとダウンロードしてきます。だいぶ時間かかるけど待つ。うまくいったらプロンプトが出てきて、sbt の流儀で開発できます。とりあえずこの状態で最低限のコードはあるので、エミュレータを起動して start-emulator と入力してやると

  • コンパイル
  • ProGuard を通す
  • パッケージ
  • エミュレータに(再)インストール
  • エミュレータ上でアプリを起動

までを行ってくれて、至れり尽くせりです。hello, world に感動。あとは src/main/ 以下にコードを書いていくだけです。

プロジェクトの定義

生成されたプロジェクトの定義は project/build/PicTumblr.scala にあります。

import sbt._

trait Defaults {
  def androidPlatformName = "android-8"
}
class PicTumblr(info: ProjectInfo) extends ParentProject(info) {
  override def shouldCheckOutputDirectories = false
  override def updateAction = task { None }

  lazy val main  = project(".", "PicTumblr", new MainProject(_))
  lazy val tests = project("tests",  "tests", new TestProject(_), main)

  class MainProject(info: ProjectInfo) extends AndroidProject(info) with Defaults with MarketPublish {
    val keyalias  = "change-me"
    val scalatest = "org.scalatest" % "scalatest" % "1.0" % "test"
  }

  class TestProject(info: ProjectInfo) extends AndroidTestProject(info) with Defaults
}

最初はまったく意味が分からなかったので自分に解説してやると、このプロジェクト "PicTumblr" には 2 つのサブプロジェクトがあって、それぞれ . にある "PicTumblr" と、それに依存していて tests/ にある "tests" だと定義されています。この親プロジェクトと子プロジェクトの名前が同じなのがややこしいし、テスト用のプロジェクトを別に作るというのも知らなかったので混乱しました。

ちなみにプラグインの定義が project/plugins/Plugins.scala にあります。

開発

細かいところでは lazy val が便利でした。あと Java の可変長引数が取れない?ところは src/java/ にクラスを Java で書いたりして対処したりとか。scala.xml が使ってる APIandroid-7 でなんかエラるとか (あきらめた)。

sbt-android-plugin についてくる TypedResouces ってやつがよくて、下のようにしてプロジェクトに TypedResources トレイトを食わせてやると

class MainProject(info: ProjectInfo) extends AndroidProject(info) with TypedResources ...

コンパイルの前に新たなアクションが実行され、src_managed/main/scala/TR.scala にコードが生成されるようになります。例えばこの中に定義されている TypedActivity トレイトの findView メソッドを使うと、型キャストなしで Activity 中の子ビューが得られてたいへん気持いい。こういうのです。

開発途中で Apache Commons Lang 使いたいなーと思ったらおもむろに

class MainProject ... {
    ...
    val commonsLang = "commons-lang" % "commons-lang" % "2.5"
}

などと書いて sbt コンソールから reload, update するだけで jar をダウンロードしてくれるので便利です!!http://repo1.maven.org/maven2/http://scala-tools.org/repo-releases/ から探してきてくれるっぽい。Apache Maven とかいうツールの仕組みみたいですね。

テスト

sbt にはデフォルトで test というアクションがあって、これで src/test/ 以下のテストを実行してくれるというので、それを使うのだろうと思ってたけどちょっと違った。単体テストはそれでいいんだけど、コード中で Log.d() とかしてるともちろん動かなくて、結局テスト自身もエミュレータ上で走らせる必要があるわけで、そのために tests というサブプロジェクトが別に用意されてます。エミュレータ上でテストを動かす test-emulator というアクションも (プラグインによって) 定義されています。

ScalaTest も、リフレクション使わない (list should be ('empty) とかしない) 限りはうまく動いたので Scala っぽいテストが書けました。ProGuard の警告がうるさいのは、設定を変えて (Project の proguardOption をオーバーライドする) で対処できます。

sbt

actions と打つと可能なアクションが列挙されるので、sbt 動きを理解するにはこれを試してみるのが早いです。project/build/Project.scala に println を仕込んでおくと、reload したときに println が呼ばれるので print デバッグ的なこともしやすい。

project/plugins 以下にプラグインのソースも一緒に落ちてきているはずなので、これを読むといろいろ参考になります (もちろん sbt のソースも読む)。

既存の Android プロジェクトを sbt 化する

というのもやってみました。

ちゃんとは検証していませんが、Java のプロジェクトに試してみたところ

  • project/build.properties
  • project/build/Project.scala
  • project/plugins/Plugins.scala

を用意してやると sbt でコンパイルできました。Project.scala はファイルの配置に合わせて

import sbt._

class Project (info : ProjectInfo) extends AndroidProject(info) {
    def androidPlatformName = "android-4"

    override def mainJavaSourcePath  = "src"
    override def mainResPath         = "res"
    override def androidManifestPath = androidManifestName
}

などと書き換えてやる必要があります。

まとめ

sbt で Android アプリを書くのはかんたん!無料です!

chmixxchmixx2011/02/19 17:22YKALoC <a href="http://oorleiuzznvh.com/">oorleiuzznvh</a>, [url=http://gcevzpifztoa.com/]gcevzpifztoa[/url], [link=http://ekgsvgllfdes.com/]ekgsvgllfdes[/link], http://bzsvvewozbuj.com/