新しく入ったReverse Binding機能を使ってPure GoでAndroidアプリを作ってみる #golang #gomobile

## はじめに

[Go Mobile](https://github.com/golang/mobile)というGoをモバイルアプリ開発に用いるというGoのサブリポジトリである`golang.org/x`以下で進められているプロジェクトがあります。

Go Mobile には、NativeアプリとSDKアプリという2種類の開発方法があり、NativeアプリはすべてをGoで書き、SDKアプリはJavaやObjective-CからGoの関数などを呼び出すというものでした。

これらの違いや詳しいやり方については、DroidKaigi 2016の[資料](http://www.slideshare.net/takuyaueda967/go-mobileandroid)やGopherCon 2016の[資料](http://www.slideshare.net/takuyaueda967/go-for-mobile-games)（[ビデオ](https://www.youtube.com/watch?v=7yfhmni2Uo8)）などを確認していただければと思います。

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のリンク](https://github.com/golang/mobile/tree/master/example/reverse)）に入っています。

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
```

実行してみると以下のような画面が出て来るはずです。

<img src="https://qiita-image-store.s3.amazonaws.com/0/14952/48714dce-d2a0-b75a-dfea-98e15045966f.png" alt="ReverseBindingExample.png" width="300px">

ぱっと見はAndroidアプリっぽい感じになってますが、Goのコードはどうなっているんでしょうか？

## ソースコードを読んでみる

Goのコードは、`example/reverse/reverse`以下に入っています。

```
$ cd $GOPATH/src/golang.org/x/mobile/example/reverse/reverse
$ ls
reverse.go
```

`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の[プロポーザル](https://github.com/golang/go/issues/16876)を読むとよいでしょう。

## まとめ
さて、いかがだったでしょうか？
GoでAndroidアプリを書くという話がだんだんと現実的なところまできてるな感じる機能です。
もちろん、まだまだクリアしないといけない課題もあり、Experimentalなプロジェクトであることは変わりませんが、今後が楽しみです。
今回説明できなかった、生成されたバインディングやどうやってバインディングを作るのかといった話やiOSについてはまた別の記事にまとめようと思います。