Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
97
Help us understand the problem. What is going on with this article?

More than 5 years have passed since last update.

@hkurokawa

go.mobile を使った Android 開発のまとめ

Go 1.4 における Android サポート

すでにご存知の方も多いと思いますが、Go 1.4 では Android が部分的にサポートされます(Go 1.4 Release Notes)。すでに、Qiita にも go.mobile を動かしてみたなどの記事が投稿されていますが、今回、もう少し踏み込んで使ってみたので、メモがてらご報告いたします。詳細については下記の参考文献をご覧ください。

参考文献

Go でできること

Go 1.4 でできる Android 開発には主に以下の2つのアプローチがあります。

  1. ネイティブアプリ。アプリを Go のみで書く。
  2. 共有ライブラリ。Go で書かれたコードを共有ライブラリにして Java から JNI 呼出しをする。

どちらも機能的にいくつか制限があるのが実情です。順に、どのように開発するのか、どのような機能が使えるのかを見ていきます。

なお、本稿では開発環境の構築手順については詳細に述べません。golang - go.mobileを動かしてみた。 - QiitaMarinX/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/libhelloMarinX/godroid が参考になると思います。

開発手順

こちらの場合、手順がやや複雑になります。

1. 実行したい Go のコードを用意する

まず、JNI 呼出しで実行したい、Go のコードを用意します。実行するメソッドは、大文字で始まる Public なものである必要があり、また引数や戻り値の型も後述する一部の型しか使えません。ここでは、与えられた値を超えない最大の素数を求めるメソッドを用意をしたとしましょう。

なお、このファイルは後で import されますので、Go のプロジェクトディレクトリの下に作成しておきましょう。ここでは $GOPATH/src/github.com/hkurokawa/godroid-sampleの直下に作成したとします。

prime.go
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 プロジェクトは図のようになっているはずです。

project_structure.png

これを起動した画面が以下のようになります。
app_screen.png

以上のコードを 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 にまとめるのはアリかもしれません。
97
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
97
Help us understand the problem. What is going on with this article?