GolangでAndroidアプリを作る

こんにちは [@wasabeef_jp](https://twitter.com/wasabeef_jp)です。

GoでAndroidアプリを作るためには
まずcgoの話からはじめて行きたいと思います。

引用：http://wasabeef.jp/goandroid/

> ### 環境

* OS :  **Mac OS X 10.9.3**
* Go : **go1.3 darwin/amd64 (brew install)**
* Android SDK : **android-20**
* Android NDK : **android-ndk-r10**
  
**執筆時点：2014/8/15**

> ### cgo 

Cgo enables the creation of Go packages that call C code

http://golang.org/cmd/cgo/

GoからC/C++にアクセスするために用いるパッケージのこと。
つまり、Cで書かれたライブラリが、Goでも再利用できる。

### 基本的な使い方
　  
まず、Cの関数や型にアクセスするために、cgoのimportを行う

```go
import "C"
```
import文の上にinclude <ほげほげ.h> と書けば
コンパイル時に自動で読み込まれるので、必要なヘッダを書く

```go
// #include <stdio.h>
// #include <stdlib.h>
import "C"
```
これで、C.int, *C.char、C.rand とかで、型や関数にアクセスできる

### GoからCを呼ぶ
　  
> Sample1 

sample1.go

```go
package main
/*
 #include <stdio.h>

 int total(int a, int b) {
  return a + b;
 }
*/
import "C"
import "fmt"

func main() {
  total := C.total(10, 20)
  fmt.Println(total)
}

```

Output

```bash
# go run sample2.go
30
```

> Sample2


コメントアウトにC言語のソースを書くとEditorとかで認識されないので、C言語は別のファイルにしても問題なく読み込まれます。

* C言語のメソッドヘッダを作成

sample2.h  

```go
#include <stdio.h>

int total(int a, int b) {
    return a + b;
}
```  

sample2.go

```go
package main
// #include "sample2c.h"
import "C"
import "fmt"

func main() {
  total := C.total(10, 20)
  fmt.Println(total)
}
```

### GoからObjective-Cを呼ぶ
　  
> Sample3

sample3.go

```go
package main
/*
#cgo CFLAGS: -x objective-c
#cgo LDFLAGS: -framework Foundation

#import <Foundation/Foundation.h>

void hello() {
    NSLog(@"Hello objective cgo world!");
}
*/
import "C"

func main() {
    C.hello()
}
```

Output

```bash
# go run sample3.go
2014-08-14 18:06:26.903 sample3objc[45354:303] Hello objective cgo world!
```

参考:  
http://unknownplace.org/archives/cgo-and-eventloop.html


## golangでAndroidアプリを作る  
　  
英語：[The State of Go Language for Android Native Development](http://www.infoq.com/news/2014/06/golang-google-android-native-dev)  
日本語：[Androidネイティブ開発に関するGo言語の状況](http://www.infoq.com/jp/news/2014/07/golang-google-android-native-dev)
　  
##### Target Version
* Android SDK : **target=android-20**
* Android NDK : **android-ndk-r10**

#### toolchainの作成

Android Toolchain Standalone Compilerとは...
Android向けのライブラリ(.so)を作るときに使うgccとかです

```bash
# $ANDROID_NDK/build/tools/make-standalone-toolchain.sh \
# --toolchain=arm-linux-androideabi-4.9 --platform=android-L --install-dir=ndk-toolchain
```

#### goandroid

[Goandroid](https://github.com/eliasnaur/goandroid)とは...  
go1.2にpatchをあてて、ARMv7+のAndroidでgoを動かせるようにしたもの
　  
#### まずは、go1.2.2をhg clone

```bash
# hg clone -u go1.2.2 https://code.google.com/p/go
```

#### goandroidをgit clone

```bash
# git clone https://github.com/eliasnaur/goandroid.git
# cp -r patches go/.hg
```

#### Mercurialの設定ファイルに追記

go/.hg/hgrc

```bash
[extensions]  
mq =
codereview = !

[ui]  
username = me<me@mail.com>
```

#### goにpatchをあてる
```language-bash
# cd go/.hg
# hg qpush -a
applying shared-lib-runtime
applying android-tls
applying android-build-hacks
now at: android-build-hacks
```

#### golang make 
```bash
# cd src
# CGO_ENABLED=0 GOOS=linux GOARCH=arm ./make.bash \
# CC="/Users/wasabeef/go/ndk-toolchain/bin/arm-linux-androideabi-gcc" \
# GOOS=linux GOARCH=arm GOARM=7 \
# CGO_ENABLED=1 ../bin/go install -tags android -a -v std  


(省略)

---
Installed Go for linux/arm in /Users/wasabeef/go/go1.2.2
Installed commands in /Users/wasabeef/go/go1.2.2/bin
```

#### version表示で動作確認
```language-bash
# ./bin/go version
go version go1.2.2 darwin/amd64
```

#### Android NativeAcivity

ANativeActivity_onCreate関数で、golangの関数を登録

main.c

```c
extern void onNativeWindowCreated(ANativeActivity *activity, ANativeWindow* window);
extern void onNativeWindowDestroyed(ANativeActivity *activity, ANativeWindow* window);
extern void onNativeWindowResized(ANativeActivity *activity, ANativeWindow* window);
extern void onInputQueueCreated(ANativeActivity *activity, AInputQueue* queue);
extern void onInputQueueDestroyed(ANativeActivity *activity, AInputQueue* queue);
extern void onCreate(ANativeActivity *activity, void* savedState, size_t savedStateSize);
extern void onDestroy(ANativeActivity *activity);
extern void onResume(ANativeActivity *activity);
extern void onPause(ANativeActivity *activity);
extern void onConfigurationChanged(ANativeActivity *activity);
extern void onWindowFocusChanged(ANativeActivity *activity, int focused);

void ANativeActivity_onCreate(ANativeActivity *activity, void* savedState, size_t savedStateSize) {
	activity->callbacks->onNativeWindowCreated = onNativeWindowCreated;
	activity->callbacks->onNativeWindowDestroyed = onNativeWindowDestroyed;
	activity->callbacks->onInputQueueCreated = onInputQueueCreated;
	activity->callbacks->onInputQueueDestroyed = onInputQueueDestroyed;
	activity->callbacks->onDestroy = onDestroy;
	activity->callbacks->onResume = onResume;
	activity->callbacks->onPause = onPause;
	activity->callbacks->onConfigurationChanged = onConfigurationChanged;
	activity->callbacks->onNativeWindowResized = onNativeWindowResized;
	activity->callbacks->onWindowFocusChanged = onWindowFocusChanged;

	onCreate(activity, savedState, savedStateSize);
}
```

登録したコールバックを実装していく

main.go

```go
package main

// #include <stdlib.h>
// #include <jni.h>
// #include <android/native_activity.h>
// #include <android/input.h>
// #include <EGL/egl.h>
// #include "main.h"
//
// #cgo LDFLAGS: -landroid -lEGL
import "C"

import (
	"fmt"
	"log"
	"runtime"
	"runtime/debug"
	"unsafe"
)

// (省略)
```

#### build.shの調整

主に、pathやarchの設定を環境によって調整

```bash
#!/bin/bash

set -e

mkdir -p android/libs/armeabi-v7a
mkdir -p android/obj/local/armeabi-v7a
CC="/Users/wasabeef/go/ndk-toolchain/bin/arm-linux-androideabi-gcc"

if [ -z $GOANDROID ]
then
  GOANDROID="../go"
fi

CC=$CC GOPATH="`pwd`:$GOPATH" GOROOT="" d=linux GOARCH=arm GOARM=7 CGO_ENABLED=1 /Users/a12622/go/go1.2.2/bin/go install $GOFLAGS -v -ldflags="-android -shared -extld $CC -extldflags '-march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16'" -tags android nativeactivity
cp bin/linux_arm/nativeactivity android/libs/armeabi-v7a/libnativeactivity.so
cp bin/linux_arm/nativeactivity android/obj/local/armeabi-v7a/libnativeactivity.so
```

#### ant build

```bash
# ant -f android/build.xml clean debug install

Buildfile: /Users/wasabeef/go/goandroid/native-activity/android/build.xml

-check-env:
 [checkenv] Android SDK Tools Revision 23.0.2
 [checkenv] Installed at /Applications/android-sdk-macosx

-setup:
     [echo] Project Name: nativeactivity
  [gettype] Project Type: Application

-pre-clean:

clean:
   [delete] Deleting directory /Users/wasabeef/go/goandroid/native-activity/android/bin
   [delete] Deleting directory /Users/wasabeef/go/goandroid/native-activity/android/gen
[getlibpath] Library dependencies:
[getlibpath] No Libraries
   [subant] No sub-builds to iterate on

-set-mode-check:

-set-debug-files:

-check-env:
 [checkenv] Android SDK Tools Revision 23.0.2
 [checkenv] Installed at /Applications/android-sdk-macosx

-setup:
     [echo] Project Name: nativeactivity
  [gettype] Project Type: Application

-set-debug-mode:

-debug-obfuscation-check:

-pre-build:

-build-setup:
[getbuildtools] Using latest Build Tools: 20.0.0
     [echo] Resolving Build Target for nativeactivity...
[gettarget] Project Target:   Android 4.4W
[gettarget] API level:        20
     [echo] ----------
     [echo] Creating output directories if needed...
    [mkdir] Created dir: /Users/wasabeef/go/goandroid/native-activity/android/bin
    [mkdir] Created dir: /Users/wasabeef/go/goandroid/native-activity/android/bin/res
    [mkdir] Created dir: /Users/wasabeef/go/goandroid/native-activity/android/bin/rsObj
    [mkdir] Created dir: /Users/wasabeef/go/goandroid/native-activity/android/bin/rsLibs
    [mkdir] Created dir: /Users/wasabeef/go/goandroid/native-activity/android/gen
    [mkdir] Created dir: /Users/wasabeef/go/goandroid/native-activity/android/bin/classes
    [mkdir] Created dir: /Users/wasabeef/go/goandroid/native-activity/android/bin/dexedLibs
     [echo] ----------
     [echo] Resolving Dependencies for nativeactivity...
[dependency] Ordered libraries:
[dependency]
[dependency] ------------------
     [echo] ----------
     [echo] Building Libraries with 'debug'...
   [subant] No sub-builds to iterate on

-code-gen:
[mergemanifest] Merging AndroidManifest files into one.
[mergemanifest] Manifest merger disabled. Using project manifest only.
     [echo] Handling aidl files...
     [aidl] No AIDL files to compile.
     [echo] ----------
     [echo] Handling RenderScript files...
     [echo] ----------
     [echo] Handling Resources...
     [aapt] Generating resource IDs...
     [echo] ----------
     [echo] Handling BuildConfig class...
[buildconfig] Generating BuildConfig class.

-pre-compile:

-compile:
    [javac] Compiling 2 source files to /Users/wasabeef/go/goandroid/native-activity/android/bin/classes

-post-compile:

-obfuscate:

-dex:
      [dex] input: /Users/wasabeef/go/goandroid/native-activity/android/bin/classes
      [dex] Converting compiled files and external libraries into /Users/wasabeef/go/goandroid/native-activity/android/bin/classes.dex...

-crunch:
   [crunch] Crunching PNG Files in source dir: /Users/wasabeef/go/goandroid/native-activity/android/res
   [crunch] To destination dir: /Users/wasabeef/go/goandroid/native-activity/android/bin/res
   [crunch] Crunched 0 PNG files to update cache

-package-resources:
     [aapt] Creating full resource package...

-package:
[apkbuilder] Current build type is different than previous build: forced apkbuilder run.
[apkbuilder] Creating nativeactivity-debug-unaligned.apk and signing it with a debug key...

-post-package:

-do-debug:
 [zipalign] Running zip align on final apk...
     [echo] Debug Package: /Users/wasabeef/go/goandroid/native-activity/android/bin/nativeactivity-debug.apk
[propertyfile] Creating new property file: /Users/wasabeef/go/goandroid/native-activity/android/bin/build.prop
[propertyfile] Updating property file: /Users/wasabeef/go/goandroid/native-activity/android/bin/build.prop
[propertyfile] Updating property file: /Users/wasabeef/go/goandroid/native-activity/android/bin/build.prop
[propertyfile] Updating property file: /Users/wasabeef/go/goandroid/native-activity/android/bin/build.prop

-post-build:

debug:

install:
     [echo] Installing /Users/wasabeef/go/goandroid/native-activity/android/bin/nativeactivity-debug.apk onto default emulator or device...
     [exec] 6040 KB/s (809846 bytes in 0.130s)
     [exec]   pkg: /data/local/tmp/nativeactivity-debug.apk


     [exec] Success

BUILD SUCCESSFUL
Total time: 1 minute 2 seconds
```

#### 雑感
ArtVMを積んでるAndroid Lでは上手くうごかなかった。  

Genymotionなどのエミュレータではx86で動いてるけど
gccのoptionで-marhc=x86とかにしても"Fatal Signal 11"系で動かなかった。

## 結論、Go+Androidは正式Supportされるまで、待つ他ないと思います。

(2014/8/15)