概要
メニューをタップ等したときに、例えば通信状態がよくない場合にエラーメッセージを出したいことがあると思いますが、JNI経由でToastを呼び出すと思わぬエラーが発生することがあります。
そういったときの対処方法のひとつを記載します。
環境
cocos2d-x(2.2.4)
原因
cocos2d-xのtouchイベントはGLThread内で処理されますが、Toastクラスは初期化時に内部でHandlerクラスの引数なしの初期化が行われており、この際に例外が送出されます。
例外が発生する該当箇所を見てみると、
Handler.java
public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
GLThread内でLooper.prepareがコールされていないようです。
対処方法
ここでは、Toastクラスの初期化がUIスレッド(mainスレッド)内で行われれば問題が発生しなさそうだということで、次のように記述してみました。
//jni経由でコールされるメソッド
Handler handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable(){
public void run(){
Toast toast = new Toast(…
…
toast.show();
}
});
かなり省略した感じですけどニュアンスは伝わると思います。
結局のところToastに限らず、touchイベント内でHandlerクラスを生成するケースがあれば全て同じ処理をする必要があります。