Go Mobileで
Androidアプリ開発
2016/02/18(木)
@Droid Kaigi 2016
The Go gopher was designed by Renee French.
The gopher stickers was made by Takuya Ueda.
Licensed under the Creative Commons 3.0
Attributions license.
ハッシュタグ:
#DroidKaigiC #gomobile
アジェンダ
■ 自己紹介
■ Goについて
● Goの特徴
■ Androidアプリ開発とGo
● Goで開発ツールを作ろう
● Android上で動くツールを作ろう
■ Go Mobile
● ネイティブアプリ
● SDKアプリ
■ まとめ
2
自己紹介
KLab株式会社
KLabGames事業本部 エンジニア
上田拓也
twitter: @tenntenn
■ 好きな言語
Go, JavaScript, Lua
■ 業務
モバイルオンラインゲームの開発(クライアントサイド)
主にゲームエンジンを使ったゲーム開発
Androidのコードを直接書くことは多くない
3
Goについて
● Goの特徴
○ 強力でシンプルな言語設計と文法
○ 並行プログラミング
○ 周辺ツールの充実
○ 豊富な標準ライブラリ
○ シングルバイナリ/クロスコンパイル
4
Goとは?
Googleが開発しているプログラミング言語
■ 特徴
● 強力でシンプルな言語設計と文法
● 並行プログラミング
● 豊富な標準ライブラリ群
● 周辺ツールの充実
● シングルバイナリ/クロスコンパイル
Goについて/Goの特徴
5
強力でシンプルな言語設計と文法
Goについて/Goの特徴
■ スクリプト言語の書きやすさ
● 冗長な記述は必要ない
■ 型のある言語の厳密さ
● 曖昧な記述はできない
6
非常に書きやすい!
並行プログラム
■ ゴールーチン
● 軽量なスレッドに近いもの
● goキーワードをつけて関数呼び出し
■ チャネル
● ゴールーチン間のデータのやり取り
● 安全にデータをやり取りできる
Goについて/Goの特徴
7
チャネル
ゴールーチン
A
ゴールーチン
B
データ
データ
go f()
豊富な標準ライブラリ
■ 標準ライブラリ一覧
 https://golang.org/pkg/
基本的なライブラリは標
準で揃っている
Goについて/Goの特徴
8
net/http HTTPサーバなど
archive, compress zipやgzipなど
crypto 暗号化
encoding JSON, XML, CSVなど
html/template HTMLテンプレート
os, path/filepath ファイル操作など
周辺ツールの充実(go tool)
■ go toolとして標準/準標準として提供
Goについて/Goの特徴
9
go build ビルドを行うコマンド
go test
xxxx_test.goに書かれたテスト
コードの実行
go doc / godoc ドキュメント生成
gofmt / goimports コードフォーマッター
golint コードチェッカー、リンター
gocode コード補完
シングルバイナリ/クロスコンパイル
■ 環境変数のGOOSとGOARCHを指定する
開発環境とは違うOSやアーキテクチャ向けに
クロスコンパイルできる
# Windows(32ビット)向けにコンパイル
$ GOOS=windows GOARCH=386 go build
# Linux(64ビット)向けにコンパイル
$ GOOS=linux GOARCH=amd64 go build
シングルバイナリになるので
動作環境を用意しなくてよい
go buildはコンパイルするコマンド
Goについて/Goの特徴
10
Goを勉強するには
■ golang.org
● 公式ドキュメント類が充実
■ コミュニティ
● Gophers Slack #japan
● Google+ Golang JP
■ Qiita
● Goタグでまとまっている
● Go言語の初心者が見ると幸せになれる場所
■ 書籍
● The Go Programming Language
● Go in Action
Goについて/Goの特徴
11
Androidアプリ開発でGoは使える?
Goについて/Goの特徴
■ 特徴
● 強力でシンプルな言語設計と文法
● 並行プログラミング
● 豊富な標準ライブラリ群
● go tool
● シングルバイナリ/クロスコンパイル
これらの特徴が
どうAndroid開発で活かせるか?
12
Androidアプリ開発とGo
● Goで開発ツールを作ろう
● Android上で動くツールを作ろう
13
Goで開発ツールを作ろう その1
簡単に画像をアップロードできて、
アプリ上から確認できるツールを作って!
非エンジニア
まだAPIサーバはできていない...
専用サーバをさくっと用意できないかな...
とあるアプリ開発の序盤にて...
Android開発とGo/Goで開発ツールを作ろう
14
エンジニア
Goで開発ツールの作り方 その1
■ 簡易コンテンツ管理システム
Android開発とGo/Goで開発ツールを作ろう
15
Android端末
確認アプリ
非エンジニアのPC
Go製のサーバ
● net/httpでHTTPサーバ
● html/templateでWebアプリ
● osでファイル操作
● archive/zipで圧縮
● imageで画像処理
開発サーバ
コンテンツのアップロード コンテンツのダウンロード
開発サーバにコンパイルして
バイナリを置き実行するだけ
net/httpパッケージを使おう
■ 簡単にHTTPサーバを作れる(コード)
func main() {
http.HandleFunc("/hello", hello)
http.ListenAndServe(":8080", nil)
}
func hello(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "hello, golang!")
} HTTPハンドラ
"/hello"のパスで
ハンドラを設定
ポート8080で
HTTPサーバを起動
Goについて/Goの特徴
$ curl http://localhost:8080/hello
hello, golang!
curlでアクセスしてみると
16
Goで開発ツールを作ろう その2
とあるアプリ開発の中盤にて...
Android開発とGo/Goで開発ツールを作ろう
17
JSONをCSVに変換したい!
Windowsで動くツールを作って!
開発環境はMacだから...
D&DしたらJSONをCSVに変換するか...
エンジニア
非エンジニア
Goで開発ツールの作り方 その2
■ Windowsでも動く変換ツール
Android開発とGo/Goで開発ツールを作ろう
18
Go製のツール
● クロスコンパイル
● encoding/json
● encoding/csv
● osでファイル操作
エンジニアの
Mac
Windows用の
バイナリを渡す
非エンジニアの
Windows
ファイルをD&Dで変換クロスコンパイル
Goで開発ツールを作ろう
■ シングルバイナリ/クロスコンパイル
● Windows, Linux, Macなどで動く
● 動作環境を準備しなくていい
■ 豊富な標準ライブラリ
● HTTPサーバが簡単につくれる
● ファイル操作や各種エンコード
● 圧縮処理や画像処理
■ 並行処理
● リソースを効率的に使う
19
Android開発とGo/Goで開発ツールを作ろう
Androidアプリ開発とGo
● Goで開発ツールを作ろう
● Android上で動くツールを作ろう
20
Android上で動かそう
■ クロスコンパイルすれば、Androidで動く
21
Android端末
開発PC
AndroidもLinuxなので
Linux用にコンパイルする
クロスコンパイル
adb push
adb shellで動かす
Android開発とGo/Android上で動くツールを作ろう
Android上で動かそう
■ クロスコンパイルする
■ adb pushで端末に送る
■ adb shellで実行権限を与えて動かす
$ GOOS=linux GOARCH=arm go build mytool.go
$ adb push mytool /data/local/tmp
$ adb shell
$ chmod 755 /data/local/tmp/mytool
$ /data/local/tmp/mytool
22
Android開発とGo/Android上で動くツールを作ろう
Android上で動くツール その1
アプリ上でダウンロードしたファイルが
サイズが0になってることがある。
他に破損してるファイルがないか調べたい。
エンジニアA
特定の端末だけ発生する...
端末内に入ってファイル1つ1つ調べるか...
エンジニアB
Android開発とGo/Android上で動くツールを作ろう
23
サイズ0のファイルを探す
24
Android開発とGo/Android上で動くツールを作ろう
Youtubeで見る コード
母艦のシェル
adb shell
端末
Android上で動くツール その2
SNSの写真をダウンロードして
リストで表示する部分のテストをしたい。
SNSへの接続部分はできてない。サイズ未定。
エンジニアA
モック画像の配信サーバを用意する...
10x10.pngがきたら10x10の画像を返す...
端末からアクセスできるサーバがないから
端末でサーバを動かすか...
エンジニアB
25
Android開発とGo/Android上で動くツールを作ろう
画像配信のモックサーバ
26
Android開発とGo/Android上で動くツールを作ろう
Youtubeで見る コード
母艦のシェル
adb shell
端末
Android上で動かすツール
■ adb shell上で使えるコマンドを作る
● 動作環境を用意しないで済む
● Android上だけで完結
● ファイル操作に関するパッケージが充実
○ os, path/filepath など
■ 端末内サーバ
● ブラウザやアプリからアクセスできる
● モックサーバ
● プロキシサーバ
○ goproxyが便利
27
Android開発とGo/Android上で動くツールを作ろう
Android上で動かすツールの注意点
■ ピュアGoでないとコンパイルが難しい
● cgoで書かれてると設定が必要
○ cgoを使うとCで書かれたライブラリが使える
○ CCにNDKのCコンパイラを指定(後述)
■ OSに依存する処理には注意
● OSに依存するライブラリ
● 環境によってサイズの異なる型の扱い
○ 環境によって32ビットだったりする
○ サイズ固定の型に書き変える
■ int32, int64, float32, float64など
28
Android開発とGo/Android上で動くツールを作ろう
Go Mobile
● cgoとGo Mobile
● SDKアプリ
● Nativeアプリ
29
cgo
■ Cで書かれたコードやライブラリが使える
import "unsafe"
/*
#include <stdio.h>
#include <stdlib.h>
void hello(char *s) { printf("Hello, %sn", s); }
*/
import "C"
func main() {
str := C.CString("Droid Kaigi")
C.hello(str)
C.free(unsafe.Pointer(str))
}
import "C"の上の
コメント内にCのコードが書ける
GoからCのコードを呼び出せる
C上扱える値はGoのGCに回収されない
Cの char * の変数を用意
30
Go Mobile/cgoとGo Mobile
コード
Android向けにcgoを使ってビルド
■ cgoのコードもクロスコンパイルできる
$ CGO_ENABLED=1
CC=arm-linux-androideabi-gcc
GOOS=android
GOARCH=arm
GOARM=7
go buid -build-mode=pie hellocgo.go
$ adb push hellocgo /data/local/tmp
$ chmod 755 /data/local/tmp/hellocgo
$ /data/local/tmp/hellocgo
Hello, Droid Kaigi
Go1.5までは
-ldflags="-extldflags=-pie"
cgoを含む場合はandroid
クロスコンパイルでcgoを使う場合
31
Go Mobile/cgoとGo Mobile
build-mode
■ どうビルドするかを指定するオプション
● archive, c-archive
○ Cアーカイブ(.a)を作る
● shared, c-shared
○ 共有ライブラリ(.so)を作る
● exec
○ 実行可能ファイル
● pie
○ PIE形式の実行可能ファイル
c-{archive, shared}は
mainパッケージが対象
Android向けにも.soファイルが作れる
32
Go Mobile/cgoとGo Mobile
cgoとNDK
33
Go Mobile/cgoとGo Mobile
Go cgo NDK
・クロスコンパイル
・共有ライブラリ
・Cのライブラリ
・JNI
・NativeActivity
Go Mobile
GoでAndroidアプリの開発
Go Mobileとは?
● Goでモバイルアプリを作るツール群
○ iOS / Androidに対応
○ golang.org/x/mobile
● 2通りのスタイル
○ SDKアプリ
⇒ Goで書いたライブラリを呼び出す
○ Nativeアプリ
⇒ Goだけで書く
参考:https://github.com/golang/go/wiki/Mobile
34
Go Mobile/cgoとGo Mobile
35
https://github.com/golang/mobile
Go Mobileのインストール
■ 事前に必要なもの
● Goの開発環境(1.5以上)
● Androidの開発環境
■ インストール
$ go get golang.org/x/mobile/cmd/gomobile
$ gomobile init -v # NDKなどをインストール
他に必要なものはこの2つのコマンドで揃う
36
Go Mobile/cgoとGo Mobile
Go Mobile
● cgoとGo Mobile
● SDKアプリ
● Nativeアプリ
37
Go
aarファイル
SDKアプリとNativeアプリ
38
Go Mobile/SDKアプリとNativeアプリ
バインディングクラス(Java)
共有ライブラリ(.so)
Java
gomobile bind
■ SDKアプリ
■ Nativeアプリ
apkファイル
Go
NativeActivity
共有ライブラリ(.so)
gomobile build
メイン
ライブラリとして
メイン
SDKアプリの例
■ Ivy big number calculator(コード)
● APLっぽい言語の処理系のアプリ
● Rob Pikeが書いた実装をライブラリとして呼び出す
● Android版とiOS版でライブラリは同じ
39
Go Mobile/SDKアプリ
Google Play App Store
gomobile bindの使い方
■ aar(Androidアーカイブ)を生成
● Goをビルドした共有ライブラリ(.so)
● バインディング(Java)をビルドした.jar
■ Android Studio(AS)のプラグインを使う
● gobindPlugin
● ASからgomobile bindを走らせる
● 生成したaarをリンク
$ gomobile bind mypkg
40
Go Mobile/SDKアプリ
JavaからGoを呼び出す
41
Go Mobile/SDKアプリ
Javaバインディング
Javaのコード
Cのコード
Goのコード
JNI
cgo
gomobile bindが
自動生成する
パッケージ
GoとJavaのバインディング
42
Go Mobile/SDKアプリ
シングルトン抽象クラス
構造体 クラス
フィールド Getter/Setter
メソッド 抽象メソッド
パッケージ関数 staticメソッド
JNIをつかった呼び出しは
Seqクラスに隠蔽されている
Go Java
gomobile bindで使えるGoの型
■ 基本的な対応型
● 符号付き整数と浮動小数点数
● ブーリアン型と文字列型
● バイトスライス([]byte)
■ 関数
● 引数と戻り値が対応型の関数
○ 戻り値は原則1つまで、error型は2つ目にできる
○ 2つ目のerror型はJavaのExceptionになる
■ 構造体
● 公開された対応型のメソッドとフィールド
■ インタフェース
● 対応型のメソッド
今後増える予定
スライス
=配列の参照型
Goは多値が返せる
43
Go Mobile/SDKアプリ
Go Mobile
● cgoとGo Mobile
● SDKアプリ
● Nativeアプリ
44
Go
aarファイル
SDKアプリとNativeアプリ
45
Go Mobile/Nativeアプリ
バインディングクラス(Java)
共有ライブラリ(.so)
Java
gomobile bind
■ SDKアプリ
■ Nativeアプリ
apkファイル
Go
GoNativeActivity
共有ライブラリ(.so)
gomobile build
メイン
ライブラリとして
メイン
Go Mobileでゲーム
■ Flappy Gopher
● GoCon キーノート
○ ソースコード
● Goだけで書かれている
● タッチイベント
● 画像の描画
● 解説記事
○ KLabGames Tech Blog
46
Go Mobile/Nativeアプリ
$ go get github.com/adg/game
$ cd $GOPATH/src/github.com/adg/game
$ gomobile build
gomobile buildの使い方
■ apkを生成
● Goをビルドした.so
● GoNativeActivityをビルドしたdex
■ インストールまで行う
● 内部adb installを使う
● Androidのみ対応
$ gomobile build mypkg
$ gomobile install mypkg
47
Go Mobile/Nativeアプリ
goコマンドでビルドするとPCでも動く
Nativeアプリの機能
■ 描画
● OpenGL ES 2(gl, exp/gl/glutil)
● 2Dシーングラフ(exp/sprite)
■ イベント
● タッチイベント(event/touch)
● ライフサイクル(event/lifecycle)
■ センサー(exp/sensor)
● 加速度、ジャイロ、磁気センサ
■ 音(exp/audio)
48
Go Mobile/Nativeアプリ
パッケージ名
Nativeアプリの基本
49
Go Mobile/Nativeアプリ
イベントループ
描画イベント
タッチイベント
ライフサイクルイベント
■ ループしながらイベントを受信して処理
イベントの種類ごとに
分岐して処理
Androidの
onStartなど
Nativieアプリの描画
■ OpenGL ES 2
● gl, exp/gl/glutil
● 自前でShaderなどを書く
■ スプライトエンジン
● exp/sprite
● 2Dシーングラフ
● OpenGLの関数を余り触らなくて良い
● お手軽に2Dゲームを作れる
(OpenGLをゴリゴリ書くよりかは)
50
Go Mobile/Nativeアプリ
スプライトエンジン
51
Go Mobile/Nativeアプリ
scene
gopher
スプライトエンジン
ground1 ・・・
ノード
テクスチャ
サブテクスチャ
ゲーム画面
描画
シーングラフ
スプライトエンジンを使った描画
52
Go Mobile/Nativeアプリ
・テスクチャのロード
・シーングラフの構築
描画イベント
ライフサイクルイベント
(画面表示開始)
・ノードの再配置
・サブテクスチャの張替え
繰り返す
初期化処理
アフィン変換行列を
セットして、座標、
角度、縮尺を設定
Nativeアプリの現状の問題点
■ Google Playで公開できない
● resディレクトリが取り込めない
○ アイコンが変えれない
● 署名なしでビルドが出来ない
■ テキスト入力
● テキスト入力が出来ない
● テキストの描画は頑張ればできる
■ プラットホームのAPIが呼べない
● Go側にインタフェースがない
53
Go Mobile/Nativeアプリ
ちょっと残念な解決策
■ resディレクトリが取り込めない
● 後からapkに入れ込む
■ 署名なしでビルドが出来ない
● 一旦消して、別のkeystoreで署名する
■プラットホームのAPIが呼べない
● 内部で使ってるcgoの領域で定義された
JavaVMオブジェクトを使う(サンプル)
● apkの中の.soを持ってきて、Android Studio
のプロジェクトに読み込ませる
せっかくapkまで作ってくれるのに!
54
Go Mobile/Nativeアプリ
Google Playで公開できました
http://github.com/tenntenn/gofun
55
Go Mobile/Nativeアプリ
GoからToastが呼び出せました
56
Go Mobile/Nativeアプリ
Nativeアプリへの願望
■ apkまで自動で行わない
● .soとjarの入ったaarを作る
○ gomobile bindと同じような方法
○ Android Studioに読み込むプラグインを用意
■ Javaを呼び出すインタフェースを公開する
● 実は存在してるけど内部パッケージ
● gomobile bindのバインディングに近い
57
Go Mobile/Nativeアプリ
しかしながら...
■ iOSのことも考える必要がある
● 同じフローでいけるのか?
● Obj-C/swiftのコードを呼べるのか?
■ GoやGo Mobileのコンセプトに沿うのか
● プラットフォームごとに違う方法は取れない
● ツール(gomobileコマンド)で面倒な処理をすべて行え
るか?
58
Go Mobile/Nativeアプリ
まとめ
■ GoはAndroid開発に使える?
● さくっと開発ツールを作る
● Android上で直接動くツール
■ Go Mobile
● まだ実験段階のプロジェクト
● C/C++で書かれていた部分が置き換わっ
ていってほしい
○ それでもcgoでCを書く必要あり
59
おまけ
■ shiny
● GUIライブラリ
● Go Mobileに似ている
● まだ始まったばかり
■ 2つのレイヤーからなる
● 基礎的な機能(ドライバ)を提供
⇒ OpenGLやX11などの実装を提供
● ピュアGoで書かれたウィジェット
⇒ 特定のドライバ実装に依存しない
サンプルをMacで動かしたところ
60
61
62
以下、補足スライド
63
SDKアプリの詳細
gomobile bindで生成されるバインディング
■ Goのコード(参考)
■ 生成されるJavaのコード
package mypkg
type Counter struct{Value int}
func (c *Counter) Inc(){c.Value++}
func New() *Counter{return &Counter{5}}
public abstract class Mypkg {
private Mypkg() {}
public static final class Counter {
public void Inc();
public long GetValue();
public void SetValue(long value);
}
public static Counter New();
}
Java
Go
自動生成
JavaからGoのコードを呼び出す
■ バインディングオブジェクト経由で呼び出す
● パッケージは抽象クラス
○ パッケージレベルの関数はパッケージクラスのメソッ
ドになる
● 構造体は内部クラス
○ メソッドは内部クラスのメソッドになる
○ フィールドはGetter/Setter付きのフィールド
Counter c = Mypkg.New();
c.Inc();
String s = String.format("%d", c.GetValue());
Log.d("Mypkg", s); // 1
Java
GoからJavaを呼び出す
66
Go Mobile/SDKアプリ
Javaバインディング
Javaのコード
Cのコード
Goのコード
インタフェースを実装
インタフェースを
引数に関数呼び出し
Javaバインディング
Javaのコード
Cのコード
インタフェースの
メソッドを呼び出し
インタフェースの
スタブは自動生成
Javaで実装したものをGoで呼び出せる
GoからJavaのコードを呼び出す
■ Go側でインタフェースとそれを使う関数を用意
■ Java側でインタフェースを実装する
public class SysPrint extends Myfmt.Printer.Stub {
public void Print(String s) {
System.out.println(s);
}
} Java
package myfmt
type Printer interface {Print(s string)}
func PrintHello(p Printer) {
p.Print("Hello, World!")
}
Go
スタブクラスが
自動生成される
GoからJavaのコードを呼び出す
■ gomobile bindで生成されるバインディング
public abstract class Myfmt {
private Myfmt() {}
public interface Printer {
public void Print(String s);
public static abstract class Stub implements Printer {
・・・
}
・・・
}
public static void PrintHello(Printer p) { ・・・ }
}
Java
GoのPrintHelloを
呼び出すメソッド
Printerを実装
GoからJavaのコードを呼び出す
Myfmt.Printer printer = new SysPrint();
Myfmt.PrintHello(printer);
Java
■ Java側からPinterを使用する関数を呼び出す
■ 手順まとめ
● Goでインタフェースとインタフェースを引数に取
る関数を用意
● gomobile bindでスタブを作る
● Java側でスタブを継承して実装する
● Java側からインタフェースを引数に取るGoの関
数を呼ぶ
SDKアプリでアプリ内サーバ
70
Go Mobile/SDKアプリ
Androidアプリ
Javaのコード
Go製アプリ内サーバ
HTTPリクエスト
アプリ内サーバへHTTPリクエストを投げ
るという選択肢も
APIサーバ
DBサーバ
HTTPリクエスト
71
Nativeアプリの詳細
app.Main
■ イベントループを行う関数
func main() {
app.Main(func(a app.App) {
// イベントループを書く
})
}
イベントループ
■ app.App.Events()からイベントを取得
// Events() <-chan interface{}
for e := range a.Events() {
switch e := a.Filter(e).(type) {
case lifecycle.Event:
// ...
case paint.Event:
// ...
}
}
テクスチャのロード
■ アセットのオープン
a, err := asset.Open("gopher.png")
■ 画像のデコード
img, err := image.Decode(a)
■ テクスチャのロード
t, err := eng.LoadTexture(img)
assetsディレクトリ以下
のファイルを見に行く
シーングラフの構築
■ ノードの作成と登録
■ 子ノードの追加
scene := &sprite.Node{}
eng.Register(scene)
n := &sprite.Node{}
eng.Register(n)
scene.AppendChild(n)
サブテクスチャの作成と設定
■ サブテクスチャの作成
st := sprite.Subtex {
T:t, // テクスチャ
R:iamge.Rect(0, 0, 100, 200)
}
■ サブテクスチャの設定
eng.SetSubTex(n, st)
テクスチャ上の該当の領
域を指定する
アフィン変換行列の設定
■ アフィン変換行列の設定
ルートノードは、サイズ:1pt x 1pt、
座標:(0,0)、角度:0に対して変換行列を掛け合わせる
// Scale(2,2)して、(5,5)だけ並行移動
eng.SetTransform(root, f32.Affine{
{2, 0, 5},
{0, 2, 5},
})
1pt = 1/72 inch
アフィン変換行列の計算
■ 親ノードから子ノードへ
親ノードの変換結果に子ノードの変換行列を
掛けわせて子ノードの描画位置を決める
root
n
eng.SetTransform(root, f32.Affine{
{2, 0, 5},
{0, 2, 5},
})
eng.SetTransform(n, f32.Affine{
{100, 0, 10},
{0, 100, 10},
})
2x2, (5, 5)
200x200, (25, 25)
ノードの配置
■ Node.Arranger.Arrange
● 毎フレーム呼ばれる
● ノードの位置を変更したりする
type Arranger interface{
Arrange(e Engine, n *Node, t clock.Time)
}

Go MobileでAndroidアプリ開発