はじめに
Go MobileというGoをモバイルアプリ開発に用いるというGoのサブリポジトリであるgolang.org/x
以下で進められているプロジェクトがあります。
Go Mobile には、NativeアプリとSDKアプリという2種類の開発方法があり、NativeアプリはすべてをGoで書き、SDKアプリはJavaやObjective-CからGoの関数などを呼び出すというものでした。
これらの違いや詳しいやり方については、DroidKaigi 2016の資料やGopherCon 2016の資料(ビデオ)などを確認していただければと思います。
Go MobileのNativeアプリでは、Goですべてを書けるものの、UIをOpenGLの関数を使って書く必要があり、せっかくAndroid APIがJava向けに提供されているにもかかわらず、Go Mobileからは直接は利用ができませんでした。
また、SDKアプリではJavaからGoを呼び出せるものの、GoからJavaのAPIを呼び出す方法がなく、NativeアプリもSDKアプリもこれじゃない感が否めませんでした。
ここでは、最近入ったJavaのAPIのバインディングを自動で作成する、Reverse Bindingの機能を使って、Javaで提供されているAndroid APIをGoから呼び出すサンプルを動かしてみます。
なお、Android SDKが必要となりますので、最新版のAndroid Studioをインストールするなりしておいて下さい。
Go Mobileのインストール
gomobile
コマンドをgo get
してきます。
$ go get -u golang.org/x/mobile/cmd/gomobile
変更したものをgo install
でビルドして反映させます。
$ go install golang.org/x/mobile/cmd/gomobile
もし、$GOPATH/bin
以下にPATH
を通してない場合は事前にパスを通して下さい。
$ export PATH=$PATH:$GOPATH/bin
gomobile init
を呼び出して、初期化を行います。
$ gomobile init -v
もし、すでに過去にGo Mobileをインストールしたことがある方は、gomobile init
の前に以下のようにキレイにしておくことをオススメします。
$ gomobile clean
サンプルを動かしてみる
今回使用するサンプルは、Go Mobileのリポジトリのexample/reverse
(Githubのリンク)に入っています。
Android Studioでexample/reverse/android
を開いてビルドする方法もありますが、ここではターミナルで直接gradle
を使ってビルドする方法を説明します。
ちなみに、Android Studioでは開いてビルドボタンを押すだけで大丈夫です。
まず、gradle
が入ってない場合はインストールします。Macの場合はbrew install
で入ります。
$ brew install gradle
続いてディレクトリを移動します。
$ cd $GOPATH/src/golang.org/x/mobile/example/reverse/android
Android SDKがインストールされている場所をANDROID_HOME
に設定します。
毎回実行するのがめんどくさい場合は、.bashrc
や.zshrc
に設定しておくと良いでしょう。
$ export ANDROID_HOME=$HOME/Library/Android/sdk
gradlew
を生成します。
$ gradle wrapper
ビルドには上記で生成したgradlew
(Windowsの場合はgradlew.bat
)を使います。
$ ./gradlew build
うまくいくとbuild/outputs/apk
にapk
ファイルができているはずです。
$ ls build/outputs/apk
android-debug.apk android-release-unsigned.apk
デバッグビルドがandroid-debug.apk
でリリースビルドがandroid-release-unsigned.apk
です。
Android機に入れて実行してみましょう。
$ adb install -r build/outputs/apk/android-debug.apk
実行してみると以下のような画面が出て来るはずです。
ぱっと見はAndroidアプリっぽい感じになってますが、Goのコードはどうなっているんでしょうか?
ソースコードを読んでみる
Goのコードは、example/reverse/reverse
以下に入っています。
$ cd $GOPATH/src/golang.org/x/mobile/example/reverse/reverse
$ ls
reverse.go
reverse.go
の中身を見てましょう。
package reverse
import (
"Java/android/databinding/DataBindingUtil"
"Java/android/os"
"Java/android/support/v7/app"
rlayout "Java/go/reverse/R/layout"
"Java/go/reverse/databinding/ActivityMainBinding"
)
type MainActivity struct {
app.AppCompatActivity
}
func (a *MainActivity) OnCreate1(this app.AppCompatActivity, b os.Bundle) {
this.Super().OnCreate1(b)
db := DataBindingUtil.SetContentView2(this, rlayout.Activity_main)
mainBind := ActivityMainBinding.Cast(db)
mainBind.SetAct(this)
}
func (a *MainActivity) GetLabel() string {
return "Hello from Go!"
}
Java/
で始まるパッケージをインポートしていることが分かります。
これらのパッケージはどこからインポートされるのでしょうか?
実はGOPATH
の中を探してもこれらのパッケージはありませんし、どこかにこれらのパッケージが公開されていてgo get
するわけでもありません。
また、よく見るとJava/
の後ろはJavaのパッケージ名またはパッケージ名+クラス名になっていることが分かります。
Reverse Bindingという機能を使って、このインポート文から自動でバインディングが生成されます。
Java/android/databinding/DataBindingUtil
のメソッドを呼び出すと、JNI経由でandroid.databinding.DataBindingUtil
が呼び出されるようなバインディングがビルド時に自動的に生成されます。
Java/go/reverse/R/layout
については、example/reverse/android/src/main/res/layout/
以下で定義されているレイアウトに対応します。ActivityMainBinding
については、Androidバインディングの機構で生成されるクラスのようです。
このサンプルを見ると、MainActivity
がapp.AppCompatActivity
を埋め込んでいます。
これは、Javaの継承に対応するコードで、onCreate
をオーバーライドしています。
onCreate
がonCreate1
となっているのは、Javaにはオーバーロードの機能があり、Goには無いため、メソッド名が被る場合は引数の個数を後ろにつけるルールでバインディングが生成されるからです。
この場合、引数が2つに見えますが、実際にはJava側のOnCreate(bundle Bundle)
に対応しているので、1
になっています。
第1引数のthis
については、他のJavaのメソッドの引数に渡すためのレシーバに対応するオブジェクトであり、この場合はDataBindingUtil.SetContentView2
やmainBind.SetAct
に渡すために使用されます。
また、Super
を呼び出すとJava上のsuper
に当たるオブジェクトを取得することができ、スーパークラスのメソッドなどを呼び出すことができます。
詳しい説明については、Reverse Bindingのプロポーザルを読むとよいでしょう。
まとめ
さて、いかがだったでしょうか?
GoでAndroidアプリを書くという話がだんだんと現実的なところまできてるな感じる機能です。
もちろん、まだまだクリアしないといけない課題もあり、Experimentalなプロジェクトであることは変わりませんが、今後が楽しみです。
今回説明できなかった、生成されたバインディングやどうやってバインディングを作るのかといった話やiOSについてはまた別の記事にまとめようと思います。