Cocos2d-xでのCCWebviewでのaccessed stale local reference

プログラムの話。
ハマっている人がたぶん多数いると思うので、ここに残しておく。
開発環境はCocos2d-x-2.2.3、Windows、Androidアプリ開発。

Cocos2d-xのAndroid環境における、Greeが開発したCCWebviewは、
AndroidのJni関係の処理に不足があって、そのままだと
CCWebviewをcreateしたあとにメンバ変数でポインタを保持して別のところで
destroyとかsetVisibilityとかの処理をしようとすると、
EclipseのLogCatに、

JNI ERROR (app bug): accessed stale local reference 0x2d00025 (index 9 in a table of size 8)

というエラーを出してハングする。

これは、jobjectの参照するJavaのオブジェクトが、GC(ガベージコレクション)されると出るメッセージで、要するにもう有効でないアドレスにアクセスしているということ。

つまりメンバにとっといたと思っていたポインタが実はJavaによって破棄されていた、という状態。

Cでjobjectの値を保持していても、GCはそれが参照されていることを知らずに、開放してしまうというわけ。

じゃあどうすればいいのか?
結論から言うと、Gree提供のwebview_pluginのコードである、

Java_org_cocos2dx_lib_Cocos2dxWebView.cpp

に1行書き加えると直る。
以下の関数に1行加えればOK。

jobject createWebViewJni(){
JniMethodInfo t;
jobject ret = NULL;
if(JniHelper::getMethodInfo(t, “org/cocos2dx/lib/gree/webview/Cocos2dxWebView”, “<init>”, “()V”)){
ret = t.env->NewObject(t.classID, t.methodID);
t.env->DeleteLocalRef(t.classID);

ret = t.env->NewGlobalRef(ret); // これを追加
}
return ret;
}

NewGlobalRef(jobject)関数は、ローカル変数をグローバル変数にする関数で、
こうすることで勝手に解放されないように指示するわけだ。
この処理の追加によってハングは回避される。

あと、NewGlobalRef(jobject)でグローバル化した変数(のポインタ)は、
不要になったときに開放しないとメモリリークの原因になるので、
以下の関数に処理を入れること。
これも、
Java_org_cocos2dx_lib_Cocos2dxWebView.cpp
内にある関数。

void destroyJni(jobject obj){
JniMethodInfo t;
if(getInstanceMethodInfo(t, obj, “destroy”, “()V”)){
t.env->CallVoidMethod(obj, t.methodID);
t.env->DeleteLocalRef(t.classID);

t.env->DeleteGlobalRef(obj); // これを追加
}
}

以下参考サイト。

一部機能だけをcocos2d-xにするには? – technica
http://technica.speee.jp/601

JNIでNewGlobalRef, DeleteGlobalRefを使う | TechRacho
http://techracho.bpsinc.jp/baba/2013_03_08/6587

JNI ERROR (app bug): accessed stale local reference | Cocos2d-x
http://www.cocos2d-x.org/forums/6/topics/50008

ほか100ページ以上参照して調べたけどこの3サイトが特に役立った。
ありがとうございます。

今回なにが苦労したかというと、Cocos2d-xの情報サイトがそんなにないのと、
CCWebviewは使ってる人が少ないのかさらに情報がない&本家も説明がほぼないこと、
Cocos2d-xがJavaとC++をまたぐ際にJniとかいうクッション技術を使っていること、
などなどが絡み合って理解しながら解決法を探したので6時間以上パソコンと
向かい合ってたよ。頼むよマジで。

この前にiPhoneで作ったアプリをAndroidに移行しようとしたら、
さまざまなソフトをバージョンを気にしながらインストールしたり、
開発環境のeclipseが正しいソースファイルなのにエラーがあると言い出したり、
makeファイルの記述から覚えないといけなかったり、
そもそもブレークポイントのデバグができなかったりで、
環境構築&ビルド成功までに2日かかった。

iPhoneの開発はペロッとなにも引っかからずにすぐできたけど
Androidの開発環境引っかかるところ多すぎっしょ。
ほんとこれ精神やられるくらい難度高いよ(笑)。

accessed stale local referenceのエラーは、
ボスのあとにラスボス出現かよ!みたいな状態だったよ。

まあでも終わってよかった。
あとは調整で終わりだ。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください