DroidKaigi 2018 では以下の枠で発表する予定。
DAY.01 (Feb 8th, 2018) 14:00-14:30 Room 7 - gomobile でゲームを作る
資料はココ → gomobile でゲームは作れる
DroidKaigi と言うからには Androider たちの集いであることは間違いないと思うんだけど、
さて、であればそもそも Go 言語が登場すること自体が結構レアな事態であり、ややもすれば
聴衆は Go 言語のこと全然知らないってこともありうるんじゃないかと想像したりしている。
ということに鑑みて、「Go とは何か」みたいなところから話題を始めようと思っているが、
だとすると 30 分ではあまり細かい話 (JNI で Toast 出すのが何故困難か、とか) まで踏み込めない構成になってしまい、
なかなか悩ましいところである。ニッチ枠であるならば、もっとニッチにニッチを煮詰めたような内容にしたいのであるが…。
ということで、ニッチにニッチを煮詰めたような内容はブログ記事に書いておき、
興味があったら見てね、みたいな感じにしとこうという結論に達した。
発表内容はライトに。リンク先はもう少し深く、みたいな。
DroidKaigi は、実は見に行くのも初めてなので、他のセッションを聞いて廻るのも楽しみである。
本日は 2018/01/29 であり、DroidKaigi 本番 (2018/02/08) までもう二週間もないところまできた。
来週の木曜日である。ヤバイ。準備捗ってない…。
とはいえ色々調査は進めている。
gomobile 関連で色々調べて分かったことを書いておく。
Toast.makeText(this, "hogehoge", Toast.LENGTH_SHORT);
「Java でやるなら」、上のような書き方をすれば Toast は出る。実に簡単。
これと同じことを gomobile with JNI でやろうと思うと、以下のようなコードになる。
(下記はエラーチェックを省略している)
先に書いておくと、下記のコードは正しく動作しない。
package toast
/*
#include <jni.h>
#include <stdlib.h>
static int
showToast(uintptr_t java_vm, uintptr_t jni_env, uintptr_t jni_ctx, char* text) {
JNIEnv* env = (JNIEnv*)jni_env;
jobject ctx = (jobject)jni_ctx;
// Toast クラスの取得 ... 1
jclass toast = (*env)->FindClass(env, "android/widget/Toast");
// makeText メソッドの ID を取得 ... 2
jmethodID makeText = (*env)->GetStaticMethodID(env, toast, "makeText",
"(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast;");
// String を生成 ... 3
jstring jstr = (*env)->NewStringUTF(env, "hogehoge");
// makeText メソッドを呼び出し ... 4
jobject toastobj = (*env)->CallStaticObjectMethod(env, toast, makeText,
ctx, jstr, 0); // 0 = Toast.LENGTH_SHORT
// show メソッド の IDの を取得 ... 5
jmethodID methodShow = (*env)->GetMethodID(env, toastobj, "show", "()V");
// show メソッドを呼び出し ... 6
(*env)->CallVoidMethod(env, toastobj, methodShow);
return 0;
}
*/
import "C"
import (
"fmt"
"github.com/pankona/gomo-simra/simra/jni"
)
type t struct{}
func NewToaster() Toaster {
return &t{}
}
func (t *t) Show(text string) error {
jni.RunOnJVM(
func(vm, env, ctx uintptr) error {
ret := C.showToast(C.uintptr_t(vm), C.uintptr_t(env), C.uintptr_t(ctx), C.CString(text))
fmt.Printf("ret = %d\n", ret)
return nil
})
return nil
}
上記のコードは、「4」のところが正しく動作しないようである。toastobj
には NULL が返却される。いまのところ原因はよく分かっていないが、makeText
の第二引数が CharSequence
であることと、そこに jstring
を渡している、ということで、型があってないというのが原因なのではないかと想像する。
jstring
は java/lang/String
と同じものという理解でおり、であれば CharSequence
として扱っても良いのではないかという気がしないでもない (String
は CharSequence
のサブクラスなので) のだが、事実、正しく動作していないところを鑑みるには、やっぱりダメなんじゃないかという気がしている。
Java のレイヤでは書ける以下のような例は、JNI のレイヤでは表現することができないと思われる。
CharSequence cs = new String("hogehoge");
ちなみに、「AndroidNDKプログラミング第二版」には、JNI から Toast を出す例が掲載されていたが、肝心の CharSequence に関しては Java のレイヤで生成されたものを JNI に引き渡す構成になっており、もう少し原因究明に届かなかった。
(2018/01/31 追記)
上記は完全にウソだった。勘違いであった。申し訳ございません!
ちゃんとどのようなエラーが (4) で吐かれているか補足してみたところ、以下の例外が投げられていた。
java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
つまり Toast を出そうとはしているようであるが、UI スレッド以外から呼び出すことはできない、ということで
Toast の出力に失敗しているようである。CharSequence
は完全に無罪であった。すまん、CharSequence
。
さて、上記のエラーを解消するためには、Toast を UI スレッド上で実行する必要がある。
定番のやり方は Handler
を用いるやり方である。例えば以下のようなやり方。
activity.runOnUiThread(new Runnable() {
public void run() {
Toast.makeText(activity, "Hello", Toast.LENGTH_SHORT).show();
}
});
これを JNI で表現するにはどうすれば良いかを考えれば良い。
無名クラスとか出てきていてやっぱり無理なんじゃないかっていう気がしているが、
とりあえず試みてみようとは思う。
上記の原因の仮説を若干補強するものとして、
CharSequence を取らずに Toast を出すメソッドを Java レイヤに準備しておき、
それを JNI から呼び出すようなコードを書いた場合は無事に Toast が出力される。
以下のようなコードをあらかじめ Java のレイヤに書いておき、
それを上記の JNI と同様のノリで呼び出すようにする。
// static Activity goNativeActivity;
// onCreate にて goNativeActivity = this;
public void showToast(final String message) {
Handler h = new Handler(goNativeActivity.getApplication().getMainLooper());
h.post(new Runnable() {
@Override
public void run() {
Toast.makeText(goNativeActivity, message, Toast.LENGTH_SHORT).show();
}
});
}
今回、DroidKaigi 向けには gomobile build を使って Go だけで Android アプリを書いてみようという試みであるので、
Java に手を入れていく手法は有り難いのであるが、追っての調査はまた別の機会にとっておくことにする。
DroidKaigi 2018 に向けての準備を改めて整理した。
プレゼンで伝えたいことは、gomobile build
で Android 向けゲームを作るときのTIPS集 みたいなことなので、
そこまで Kokeshi Scramble はフィーチャーしないほうがいいよね、と思いを改め。
gomobile build
でいきなり APK 吐けて楽なのは良いものの、いざ作っていくとこんなハマり事があるよ、っていう紹介。みたいな感じの流れが良いかな、ということをうすらぼんやり考え。
だいたい残り 1 ヶ月であり、作業に使えない日もあるだろうことを考えると、
作業に使える日数は 20 日程度と考えるべきかと思う。
1 日 1 〜 2 時間捻出したら 20 〜 40 時間。だいたい 3 〜 6 人日ってところだろうか。
ここに収まる中で準備を進めないといけないが、以下のような対応をしておきたい。
トータルで 5 人日くらいになりそうな作業見積だがどうか。
Kokeshi Scramble の改善まではちょっと手が付けられないだろうという感じかな。
という感じで作業を何となくでも洗い出しておくと、いざ着手したときにスムーズに作業が進められるよね、という期待を込めて。
DroidKaigi、気合い入れていくぞ。
DroidKaigi 2018 にはしゃべる側で参加する予定である。
以下、準備しておこうと思う内容のメモ。
DroidKaigi までに行う Kokeshi Scramble の改修は以下を想定。
その他、可能であれば行いたい細かい点の改善。
DroidKaigi までもう残り一ヶ月程度。
コツコツやっていくぞー。今年も頑張るぞー。