ScalaでAndroid 2012年冬
このエントリはAndroid Advent Calendarの6日目裏です。今日の表は@ngsw_taroさんです。
さて、AndroidといえばScalaやKotlin、Haxeなど*1で書くことが多いと思いますが、今回はその中でも割とメジャーなScalaでのやり方についてまとめてみます。
Scalaで書くための手法はいくつかありますが、今回は現在最も主流である(と思われる) sbt android plugin (https://github.com/jberkel/android-plugin) を使った方法について書きます。
開発の流れとしてはターミナルでアプリケーションの作成とテスト、ビルドをして、コードはIDEとかで書く感じになります。
環境準備
何はともあれ開発環境を整える。入れなければならないのは以下の通り。
Scala本体とandroid sdkはいいとして、sbtはMavenっぽいScalaのビルドツール、giter8はプロジェクトのひな形を作るためのツールです。
いずれもTypesafe — Stack: Downloadに沿ってインストールしておきます。Macなら全てhomebrewで入るのでぬるげーです。ちなみに今回使用したバージョンは以下になります。
λ ~/sandbox/ → scala -version Scala code runner version 2.9.2 -- Copyright 2002-2011, LAMP/EPFL λ ~/sandbox/ → sbt sbt-version [info] Loading global plugins from /Users/takashabe/.sbt/plugins [info] Set current project to default-171b7b (in build file:/Users/takashabe/sandbox/) [info] 0.12.1 λ ~/sandbox/ → g8 giter8 0.4.5
android sdkは普通にインストールしておき、pathを通して環境変数を作っておきます。r21を使っています。
λ ~/ → cat .zshrc_custom ## いろいろ略 # ANDROID-SDK export ANDROID_SDK_HOME=/Applications/android-sdk-macosx/ export PATH=/Applications/android-sdk-macosx/platform-tools:/Applications/android-sdk-macosx/tools:$PATH
プロジェクトを作る
何はともあれgiter8でひな形を作っておきます。注意点としてはProguardをtrueにしないとコンパイルに失敗します。
λ ~/sandbox/ → g8 jberkel/android-app Template for Android apps in Scala package [my.android.project]: com.takashabe.sample name [My Android Project]: sample main_activity [MainActivity]: scala_version [2.9.2]: api_level [10]: useProguard [true]: scalatest_version [1.8]: Applied jberkel/android-app.g8 in sample λ ~/sandbox/ → cd sample λ ~/sandbox/sample/ → ls project src tests
この状態で一度動かしてみます。テストとかビルドとかは全てsbt経由で行います。
android:package-debugコマンドでコンパイルして、android:start-deviceでapkを流し込むっぽいことをやってます。
なお、エミュレータで動かす場合はandroid:start-deviceをandroid:start-emulatorとすれば良いだけです。
λ ~/sandbox/sample/ → sbt [info] Loading global plugins from /Users/takashabe/.sbt/plugins [info] Loading project definition from /Users/takashabe/sandbox/sample/project [info] Updating {file:/Users/takashabe/sandbox/sample/project/}default-883c40... [info] Resolving org.scala-sbt#precompiled-2_10_0-m7;0.12.1 ... [info] Done updating. [info] Compiling 1 Scala source to /Users/takashabe/sandbox/sample/project/target/scala-2.9.2/sbt-0.12/classes... [info] Set current project to sample (in build file:/Users/takashabe/sandbox/sample/) > ;android:package-debug;android:start-device ## 略 [info] Dexing /Users/takashabe/sandbox/sample/target/classes.dex [info] Packaging /Users/takashabe/sandbox/sample/target/sample-0.1.apk [success] Total time: 19 s, completed Dec 5, 2012 2:15:11 AM [info] Wrote /Users/takashabe/sandbox/sample/target/scala-2.9.2/src_managed/main/scala/com/takashabe/sample/TR.scala ProGuard, version 4.6 ProGuard is released under the GNU General Public License. You therefore must ensure that programs that link to it (scala, ...) carry the GNU General Public License as well. Alternatively, you can apply for an exception with the author of ProGuard. The output seems up to date [info] Packaging /Users/takashabe/sandbox/sample/target/sample-0.1.apk [info] pkg: /data/local/tmp/sample-0.1.apk [info] Success [info] 3127 KB/s (177013 bytes in 0.055s) [info] Starting: Intent { act=android.intent.action.MAIN cmp=com.takashabe.sample/.MainActivity } [success] Total time: 3 s, completed Dec 5, 2012 2:15:14 AM >
Hello, world!
この時点でビルドに失敗する場合はproject/plugin.sbtとかBuild.scalaで指定されているpluginのバージョンが間違っている可能性があります。っていうか以前はよくありました。
IDEとの連携
ビルド出来ることは分かったので、次はIDE上にプロジェクトをimportする方法について見ていきます。
sbtでは各種IDE用の設定ファイルを生成するためのPluginが存在するので、まずはそのPluginを使うようにします。
以下のようにplugin.sbtに下4行を追加すればいいだけですが、空行を開ける必要があるので注意です。1.2.0-SNAPSHOTの部分は適宜最新バージョンを指定してください。*2
ちなみにプロジェクトごとに毎回書くのがだるい場合は~/.sbt/plugins/build.sbtあたりに書いておけばプロジェクト全体に勝手に適用されます。
diff --git a/project/plugins.sbt b/project/plugins.sbt index e3163f8..a7580b7 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,3 +1,7 @@ resolvers += Resolver.url("scalasbt releases", new URL("http://scalasbt.artifactoryonline.com/scalasbt/sbt-plugin-releases"))(Resolver.ivyStylePatterns) addSbtPlugin("org.scala-sbt" % "sbt-android-plugin" % "0.6.2") + +resolvers += "Sonatype snapshots" at "http://oss.sonatype.org/content/repositories/snapshots/" + +addSbtPlugin("com.github.mpeltonen" % "sbt-idea" % "1.2.0-SNAPSHOT")
次にsbtを起動した時にPluginのインストールが始まるので、以下のコマンドでIDE用のファイルが生成されます。
> gen-idea ## 色々生成している > exit λ ~/sandbox/sample/ → λ git master* → l total 0 drwxr-xr-x 9 takashabe staff 306 Dec 6 20:12 . drwxr-xr-x 3 takashabe staff 102 Dec 5 02:07 .. drwxr-xr-x 13 takashabe staff 442 Dec 6 20:16 .git drwxr-xr-x 11 takashabe staff 374 Dec 6 20:12 .idea drwxr-xr-x 5 takashabe staff 170 Dec 6 20:12 .idea_modules drwxr-xr-x 6 takashabe staff 204 Dec 6 19:58 project drwxr-xr-x 4 takashabe staff 136 Dec 5 02:07 src drwxr-xr-x 12 takashabe staff 408 Dec 6 20:12 target drwxr-xr-x 4 takashabe staff 136 Dec 6 20:12 tests
あとはIDEA上でプロジェクトをopenすればOKです。Scala Pluginはインストールしておく必要があります。
syntax highlightされなかったりpathが見つからないとか言われた時はProject StructureでJDK, Android jar, Scalaのpathを編集すれば何とかなるはずです。。。この記事書いてる時点でクリーンな環境が無かったのであばばば
とりあえず今日発表されたばかりのIDEA 12でも動作してます。
継続的にビルドする
sbtではファイルを監視して変更があれば任意のコマンドを実行する機能があります。先頭に ~ つけるだけです。ちなみにセミコロンはコマンドの区切りです。
> ~;android:package-debug;android:start-device
実行するコマンドは何でも良いので ~test とかやれば自動的にテストを回せて素敵です。
ただし、project配下のファイルの変更は監視しないのでplugin.sbtとかに設定を追加した場合は手動でupdate/reloadする必要があります。
Google Playにデプロイする
ではこのsampleアプリもGoolge Playでヒットするアプリに育ったのでデプロイしてみようと思います。
証明書を作る工程は通常と同じです。*3 今回はalias_nameで証明書を作っているものと仮定して進めます。
証明書を作ったらproject/Build.scalaのkey値を証明書に合わせて変更します。
diff --git a/project/Build.scala b/project/Build.scala index 13dc305..423ec0c 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -23,7 +23,7 @@ object General { proguardSettings ++ AndroidManifestGenerator.settings ++ AndroidMarketPublish.settings ++ Seq ( - keyalias in Android := "change-me", + keyalias in Android := "alias_name", libraryDependencies += "org.scalatest" %% "scalatest" % "1.8" % "test" ) }
次にsbt上で署名してrelease用のapkを生成します。
target/sample-0.1-market.apkがrelease用のapkです。
> ;android:package-release;android:prepare-market ## 略 Enter password for keystore/alias_name: ********** [info] Signed /Users/takashabe/sandbox/sample/target/sample-0.1.apk [info] Aligned /Users/takashabe/sandbox/sample/target/sample-0.1-market.apk [success] Ready for publication: [success] /Users/takashabe/sandbox/sample/target/sample-0.1-market.apk [success] Total time: 6 s, completed Dec 6, 2012 9:24:50 PM
ただ、この状態でGoogle Playにデプロイしようとするとエラー内容もなく怒られちゃいます。
これは以前どハマりしたのですが、どうやらデフォルトのiconのままだとダメっぽい。なので適当な画像を用意してmanifestを書き換えます。
画像を置く場所はいつもどおりres以下に置きます。
λ ~/sandbox/sample/src/main/res/ → λ git master* → l total 0 drwxr-xr-x 8 takashabe staff 272 Dec 6 21:33 . drwxr-xr-x 6 takashabe staff 204 Dec 6 21:35 .. drwxr-xr-x@ 145 takashabe staff 4930 Dec 6 21:33 drawable-hdpi drwxr-xr-x 3 takashabe staff 102 Dec 6 21:33 drawable-ldpi drwxr-xr-x 3 takashabe staff 102 Dec 6 21:33 drawable-mdpi drwxr-xr-x 3 takashabe staff 102 Dec 6 21:33 drawable-xhdpi drwxr-xr-x 3 takashabe staff 102 Dec 5 02:07 layout drwxr-xr-x 3 takashabe staff 102 Dec 5 02:07 values
AndroidManifest.xmlも上記の画像を読み込むように変更。
diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml index 62f45c8..f7f1b26 100644 --- a/src/main/AndroidManifest.xml +++ b/src/main/AndroidManifest.xml @@ -2,7 +2,7 @@ package="com.takashabe.sample"> <application - android:icon="@drawable/android:star_big_on" + android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:debuggable="true">
もう一度release用のapkを作成して、再度デプロイ!
そういえばバージョン0.1のままでしたので怒りを抑えて整数に直します。
ここで普通ならばAndroidManifest.xmlを直接編集しますが、sbtプロジェクトの場合はproject/Build.scalaのkeyを編集します。*4
でもさっきiconを変えるときにmanifest見たけどバージョンかかれてなかったぞ・・・?とか思われるかもしれませんが、src/main/AndroidManifest.xmlは最終的なmanifestではなくてコンパイル時にsbtがパースして別のmanifestを生成します。*5
diff --git a/project/Build.scala b/project/Build.scala index 423ec0c..2bfbd80 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -6,8 +6,8 @@ import AndroidKeys._ object General { val settings = Defaults.defaultSettings ++ Seq ( name := "sample", - version := "0.1", - versionCode := 0, + version := "1.0", + versionCode := 1, scalaVersion := "2.9.2", platformName in Android := "android-10" )
これでもう一度apkを生成してデプロイします。
やりました。メガヒット間違いなしですね。*6
ほんとはテストとかjenkinsとかも書きたかったのですが長くなってきたのと体調がひどい感じなのでここで終わります。テストとかはまた今後書くかも。
明日のAdvent Calendarは表 @chun_ryoさん、裏 @Arigata3さんです。たのしみ!