Go 1.4 における Android サポート
すでにご存知の方も多いと思いますが、Go 1.4 では Android が部分的にサポートされます(Go 1.4 Release Notes)。すでに、Qiita にも go.mobile を動かしてみたなどの記事が投稿されていますが、今回、もう少し踏み込んで使ってみたので、メモがてらご報告いたします。詳細については下記の参考文献をご覧ください。
参考文献
- [Go Support for Android] (https://docs.google.com/document/d/1N3XyVkAP8nmWjASz8L_OjjnjVKxgeVBjIsTr5qIUcA4/edit)
- [Binding Go and Java] (https://docs.google.com/document/d/1y9hStonl9wpj-5VM-xWrSTuEJFUAxGOXOhxvAs7GZHE/edit)
- Go 1.4 Release Notes
- mobile - GoDoc
- MarinX/godroid
- golang - go.mobileを動かしてみた。 - Qiita
Go でできること
Go 1.4 でできる Android 開発には主に以下の2つのアプローチがあります。
- ネイティブアプリ。アプリを Go のみで書く。
- 共有ライブラリ。Go で書かれたコードを共有ライブラリにして Java から JNI 呼出しをする。
どちらも機能的にいくつか制限があるのが実情です。順に、どのように開発するのか、どのような機能が使えるのかを見ていきます。
なお、本稿では開発環境の構築手順については詳細に述べません。golang - go.mobileを動かしてみた。 - Qiita や MarinX/godroid を参考に行ってみてください。
参考までに、筆者がハマった点としては、以下のようなものがあります。御参考になれば幸いです。
- Go がすでに入っている場合は、環境変数の
PATH
を設定して、自分でビルドした android 向けのgo
コマンドが使われるようにすること - (当たり前だが)エミュレータの ABI に ARM を指定すること。x86 を指定するとネイティブライブラリが見付からないというエラー(UnsatisfiedLinkError)が出ます。
ネイティブアプリ
ネイティブアプリの場合、main
関数の中で app.Run()
を呼ぶ必要があります。描画関数や、タッチイベントのコールバック関数は、このときに引数として渡します。
func main() {
app.Run(app.Callbacks{
Draw: draw,
Touch: touch,
})
}
基本的には mobile/gl
パッケージを使うことで、Open GL ES 2 の機能を使うことになります。機能は意図的に最小限にしてあり C API とできるだけ近くなるようにしてあるそうです。残念ながら筆者は OpenGL の経験が皆無なので、これが必要十分な機能なのか分かりませんが、ライブラリや開発環境が充実しないとなかなか辛いのではないかと思われます。
こちらのアプローチでは、アプリをすべて Go で書くことになります。ただし、Activity は1つしかありませんし、Android SDK の機能なども使えません。基本的にはゲーム開発を対象としているようです。現状でも NDK を使った OpenGL ベースの Android ゲームが開発されていますが、それらと同等のものと思って良いでしょう。
開発手順
手順については、サンプルのgolang.org/x/mobile/example/basic/make.bash
が参考になるでしょう。手順としては単純で、以下のように go build
で共有ライブラリを作った後に ndk-build
コマンドで NDK をビルドしているだけです。
CGO_ENABLED=1 GOOS=android GOARCH=arm GOARM=7 go build -ldflags="-shared" -o jni/armeabi/libbasic.so .
ndk-build NDK_DEBUG=1
ant debug
共有ライブラリ
このアプローチでは、アプリは通常の Java で書き、Go を使いたいところだけ JNI 呼出しで Go のコードを実行する、という形になります。サンプルとしては、golang.org/x/mobile/example/libhello
や MarinX/godroid が参考になると思います。
開発手順
こちらの場合、手順がやや複雑になります。
1. 実行したい Go のコードを用意する
まず、JNI 呼出しで実行したい、Go のコードを用意します。実行するメソッドは、大文字で始まる Public なものである必要があり、また引数や戻り値の型も後述する一部の型しか使えません。ここでは、与えられた値を超えない最大の素数を求めるメソッドを用意をしたとしましょう。
なお、このファイルは後で import
されますので、Go のプロジェクトディレクトリの下に作成しておきましょう。ここでは $GOPATH/src/github.com/hkurokawa/godroid-sample
の直下に作成したとします。
package prime
func MaxPrime(n int32) int32 {
...
return p
}
2. binding 用の Java コードと Go コードを生成する
次に、Java と Go でデータをやりとりするための proxy コードを生成します。gobind
コマンドを使用しますので、インストールがまだでしたら、先にインストールしておく必要があります。
$ go install golang.org/x/mobile/cmd/gobind
続いて、以下のコマンドを実行します。
$ mkdir go_prime
$ gobind -lang=go github.com/hkurokawa/godroid-sample > go_prime/go_prime.go
$ gobind -lang=java github.com/hkurokawa/godroid-sample > Prime.java
生成された Go コードのパッケージが go_<元になったGoコードのパッケージ名>
になっていることに注意しましょう。
3. 共有ライブラリの生成
続いて、共有ライブラリを生成します。まず、エントリポイントとなる main.go
を作成する必要があります。適当なディレクトリを作成(たとえば、go_prime_lib
)して、その下に以下のようなファイルを作成します。ブランク識別子を指定した import
文で、先程作成した go プログラムと Java バインディングパッケージを読み込んでいる点に注意しましょう。公式ドキュメントでは、プログラムが終了するまで return
しないメイン関数を書けと言っているのですが、手元で試した限りはapp.Run
を呼ばないといけないようです。
package main
import (
"golang.org/x/mobile/app"
_ "github.com/hkurokawa/godroid-sample/go_prime"
_ "golang.org/x/mobile/bind/java"
)
func main() {
app.Run(app.Callbacks{})
}
そして、以下のコマンドを実行します。
CGO_ENABLED=1 GOOS=android GOARCH=arm GOARM=7 go build -ldflags="-shared" .
すると、go_prime_lib
のようなライブラリファイルが出来るので、gojni.so
にリネームして、Android プロジェクトの src/main/jniLibs/armeabi-v7a
の下に移動させます。なお、このファイル名は、いまのところ固定のようです。変更したい場合は、後述の Go.java
の中身を書き換える必要がありますが、公式ドキュメントには何も触れられていないので、とりあえず従っておく方が良いでしょう。
4. 必要な Java ファイルのコピーおよびリンク
これで終わりかというと、残念ながら、まだ1ステップ残っています。Go のライブラリを読み込む Java コードや、自動生成した Java ファイルをプロジェクトに含める必要があるのです。
まず、生成した Java コードを Android プロジェクトのソースディレクトリに移動しましょう。続いて、go.mobile パッケージ内の Java ファイルもプロジェクトのソースディレクトリに含めます。こちらは、コピーしても良いですが、サンプルに倣ってシンボリック・リンクを張るのが良いでしょう。この例では以下の bash スクリプトを github.com/hkurokawa/godroid-sample
で実行しました。
ANDROID_APP=$PWD/android_studio_app/GodroidSample/app
(cd ../../../golang.org/x/mobile/ && ln -sf $PWD/bind/java/Seq.java $ANDROID_APP/src/main/java/go)
(cd ../../../golang.org/x/mobile/ && ln -sf $PWD/app/*.java $ANDROID_APP/src/main/java/go)
プロジェクトの構成
以上の作業によって、Android プロジェクトは図のようになっているはずです。
以上のコードを GitHub に公開しましたので、良かったら参考にしてください。
https://github.com/hkurokawa/godroid-sample
まとめ
以下、現状の go.mobile を触ってみた感想です。
- まだ、Go だけで開発するのは厳しそう
- これは当然ですが、まだライブラリやサポートが整っていないので、いますぐに Go で Android アプリの開発というのは時期尚早でしょう。個人的には、画像処理をGo でやりたかったのですが、JNI 呼び出しの引数や戻り値で配列型を使えないのはツラいです。
- Lollipop では動かない
- 現状では、サンプルも含め、Lollipop では動きませんでした。こちらについては、そのうち解決すると思いますが。
- Android Studio / Gradle サポートがほしい
- 現状は、手順がかなり複雑になっていますが、将来的には Android Studio 上で開発できるようにするそうです。binding のソースコードも自動生成なので、そうなると、かなり効率よく開発できそうです。
- ゲーム開発がメイン?
-
2014-11-25 追記: JNI 呼び出しのオーバーヘッドですが、これは自分の当初使用した Go プログラムのアルゴリズムの問題のせいで、オーバーヘッド自体は微々たるものでした。適当な記述を書いて申し訳ありませんでした。
Go Support for Androidには主にゲーム開発を対象とするとあります。実際、自分で JNI 呼び出しのアプリを書いてみると、思っていたよりも遅い印象がありました。実装などにもよると思いますが、頻繁に JNI 呼び出しを行うのはあまり想定されていないのかもしれません。このあたりは、もうすこし調査します。 - Go で書けるのはうれしい
- とはいえ、いち Go ファンとしては、Go で Android アプリを書けるのは楽しいです。クロスコンパイルも楽なので、iOS もサポートされれば、両方で必要なライブラリを Go にまとめるのはアリかもしれません。