GomobileからAARを作ってUnityで動かしてみた

## 概要

[Go](https://golang.org/)にはAndroidとiOS向けにアプリケーションやライブラリを出力する[gomobile](https://github.com/golang/mobile)というライブラリがあります。
Goはワンプロセス、マルチスレッド、スレッド間通信を言語レベルで実装しており、スレッドプログラムを安全にかつ、非常に簡単に書ける特徴があり、Unityでのスマフォアプリ開発に利用できればと思い評価しました。

今回の検証では、UnityでGoが動作するかの確認までとし、Goのスレッドの恩恵を受けれるかを評価した内容ではありません。

## 今回の調査後に下記も書きました

* [Gomobileで作ったAARをばらしてShared LibraryだけをUnityで動かしてみた
](http://qiita.com/otmb/items/e4bb761e4f75c0730095)
* [GomobileからShared libraryを出力してみた](http://qiita.com/otmb/items/6a59951267d384052b59)

## 今回の実現方法

[gomobile](https://github.com/golang/mobile)で作成したaar(Android ARchive)とAndroid Studioで作成したaarをUnityのPluginに放り込むことで実現します。
とても黒魔術な香り。

何故このやり方になるかというとgomobileのbindはabstract classが出力される為、
Unityから直接呼び出せなくて一度書き直す必要がありました。
何か他にいい方法あれば教えてください。

gomobileとAndroid StudioとUnityのインストール手順は割愛。

もっとスマートに出来るといいけど、とりあえず今日はハックするとこまで。


## gomobileでaar作成

```
$ mkdir $GOPATH/src/hoge
$ cd $GOPATH/src/hoge
```
hoge.goを作成

```go:hoge.go
package hoge 

func GetInt() int {
    return 10
}
```

```
$ gomobile bind -target=android .
```
`hoge.aar` が出力される

中身を見るとCPU毎に`libgojni.so`(Shared Library)が出力となっている。

```
$ unzip -Z1 hoge.aar 
AndroidManifest.xml
proguard.txt
classes.jar
jni/armeabi-v7a/libgojni.so
jni/arm64-v8a/libgojni.so
jni/x86/libgojni.so
jni/x86_64/libgojni.so
R.txt
res/
```

上の`classes.jar`の中身はこのようになっている。

```
META-INF/MANIFEST.MF
go/LoadJNI.class
go/Seq$1.class
go/Seq$Proxy.class
go/Seq$Ref.class
go/Seq$RefMap.class
go/Seq$RefTracker.class
go/Seq.class
go/Universe$proxyerror.class
go/Universe.class
go/error.class
go/hoge/Hoge.class
```

## Android Studioでaar作成


1. `New Project`で`Add No Activiry`を作成
 * minSdkVersion=15とした
2. `New Module`で`Android Library`を作成
 * Module名は`hogedroid`にした。
3. gomobileで作成した`hoge.aar`を`libs`にドラッグ&ドロップする
4. build.gradleに下記を追記

```:build.gradle(hogedroid)
dependencies {
    compile fileTree(include: ['*.jar'], dir: 'libs')
    compile (name: 'hoge', ext:'aar')
}

repositories{
    flatDir{
        dirs 'libs'
    }
}
```
5 メイン処理の作成

```java:HogeClass.java

package com.example.manabu.hogedroid;
import go.hoge.Hoge;

public class HogeClass {

    public static long get() {
        return Hoge.getInt();
    }

}
```
 * パスが通っていれば⌘Bで定義にジャンプして`hoge.aar`の中身が見れる
 
```go:hoge.aar
package go.hoge;

import go.Seq;

public abstract class Hoge {
    private Hoge() {
    }

    public static void touch() {
    }

    private static native void _init();

    public static native long getInt();

    static {
        Seq.touch();
        _init();
    }
}
```

Unityから直接abstract呼べたらいいのに。
ここでは、頭文字大文字で書いてたメソッドが小文字になってたりする。
他にもintがlongになっていた。
罠でしかない。

### 操作箇所にコメントした画像

<img src="https://qiita-image-store.s3.amazonaws.com/0/35973/36ad49c0-80a5-aee1-7c5f-ed4bd7486caa.png" width="400px">

### Gradleからassembleをダブルクリックで実行してaar作成

<img src="https://qiita-image-store.s3.amazonaws.com/0/35973/83c3da17-a35d-1a14-e516-661de548c0cb.png" width="400px">


```
% unzip -Z1 hogedroid-debug.aar 
aapt/
aapt/AndroidManifest.xml
AndroidManifest.xml
classes.jar
R.txt
res/
res/values/
res/values/values.xml
aidl/
assets/
jni/
libs/
```

## Unityに適当なコール処理を書く

* `hoge.aar`と`hogedroid-debug.aar`を`Assets/Plugins/Android`にドラッグして配置する

```
using UnityEngine;
using System.Collections;

public class ButtonTest : MonoBehaviour
{
	long gnum = 0;
	public void TestClick(){
		#if UNITY_ANDROID

		using (AndroidJavaClass jc = new AndroidJavaClass("com.example.manabu.hogedroid.HogeClass"))
		{
			Debug.Log(jc);
			long num = jc.CallStatic<long>("get");
			Debug.Log(num);
			gnum += num;

		}
		#endif
	}

	void OnGUI(){
		GUI.Label (new Rect (0, 0, 100, 30), gnum.ToString());
	}
}
```

うまくビルドできたら動くはず。

## あとがき

本当はAppleのTLS1.2対応の話があったので、Golang使ったhttp2+TLS1.2をUnityで動かすネタをやろうと思ったがここまでで結構大変なのでやめた。

Unityは.NET3.5で動いていて、TLS1.0までしか対応してないのでいいネタになると思ったんだけどなぁ。


## おまけ:http2 serverとclient

参考: https://golang.org/pkg/net/http/
go v1.6系ならこの書き方でいける

* pemのcommon nameだけ設定するようなの

```
$ go run /usr/local/go/src/crypto/tls/generate_cert.go --host localhost 
```


```go:httpServer.go
package main

import (
    "net/http"
    "log"
)

func handler(w http.ResponseWriter, req *http.Request) {
    w.Header().Set("Content-Type", "text/plain")
    w.Write([]byte("This is an example server.\n"))
}

func main(){

    http.HandleFunc("/", handler)
    log.Printf("About to listen on 10443. Go to https://localhost:10443")
    err := http.ListenAndServeTLS(":10443", "cert.pem", "key.pem", nil)
    log.Fatal(err)
}
```


```go:httpClient.go
package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
    "crypto/tls"
)

func main() {

   tr := &http.Transport{
        TLSClientConfig: &tls.Config{
            InsecureSkipVerify: true,
        },
    }

    client := &http.Client{Transport: tr}
    resp, err := client.Get("https://localhost:10443/")
    if err != nil {
		log.Fatal(err)
	}

	robots, err := ioutil.ReadAll(resp.Body)
	resp.Body.Close()
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("%s", robots)
}
```

## ちょこちょこ罠がある

* うちのMacBookProだとgccのビルドに1時間かかった
* Android Studioの謎エラーの調整、ほんと謎だわ

```
Theme.AppCompat.Light.DarkActionBar
↓
android:Theme.Holo.Light.DarkActionBar
```
* Gradleからassembleの実行が謎エラーになる時はプロジェクト直下にあるコマンドを叩くとうまくいく

```
$ ./gradlew assembleRelease`
```