こんにちは @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
GoからC/C++にアクセスするために用いるパッケージのこと。
つまり、Cで書かれたライブラリが、Goでも再利用できる。
基本的な使い方
まず、Cの関数や型にアクセスするために、cgoのimportを行う
import "C"
import文の上にinclude <ほげほげ.h> と書けば
コンパイル時に自動で読み込まれるので、必要なヘッダを書く
// #include <stdio.h>
// #include <stdlib.h>
import "C"
これで、C.int, *C.char、C.rand とかで、型や関数にアクセスできる
GoからCを呼ぶ
Sample1
sample1.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
# go run sample2.go
30
Sample2
コメントアウトにC言語のソースを書くとEditorとかで認識されないので、C言語は別のファイルにしても問題なく読み込まれます。
- C言語のメソッドヘッダを作成
sample2.h
#include <stdio.h>
int total(int a, int b) {
return a + b;
}
sample2.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
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
# 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
日本語:Androidネイティブ開発に関するGo言語の状況
Target Version
- Android SDK : target=android-20
- Android NDK : android-ndk-r10
toolchainの作成
Android Toolchain Standalone Compilerとは...
Android向けのライブラリ(.so)を作るときに使うgccとかです
# $ANDROID_NDK/build/tools/make-standalone-toolchain.sh \
# --toolchain=arm-linux-androideabi-4.9 --platform=android-L --install-dir=ndk-toolchain
goandroid
Goandroidとは...
go1.2にpatchをあてて、ARMv7+のAndroidでgoを動かせるようにしたもの
まずは、go1.2.2をhg clone
# hg clone -u go1.2.2 https://code.google.com/p/go
goandroidをgit clone
# git clone https://github.com/eliasnaur/goandroid.git
# cp -r patches go/.hg
Mercurialの設定ファイルに追記
go/.hg/hgrc
[extensions]
mq =
codereview = !
[ui]
username = me<me@mail.com>
goにpatchをあてる
# cd go/.hg
# hg qpush -a
applying shared-lib-runtime
applying android-tls
applying android-build-hacks
now at: android-build-hacks
golang make
# 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表示で動作確認
# ./bin/go version
go version go1.2.2 darwin/amd64
Android NativeAcivity
ANativeActivity_onCreate関数で、golangの関数を登録
main.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
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の設定を環境によって調整
#!/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
# 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)