Android2.2,2.3,2.3.3都存在的一個bug,在這裡有詳細的描述。
http://code.google.com/p/android/issues/detail?id=12987
其中,樓主的代碼可以下來運行一下,我在2.2和2.3模擬器和2.3的真機上都存在這個問題。
問題的核心在於這樣一句話:JNI WARNING: jarray 0x4074f6e0 points to non-array object (Ljava/lang/String;)
也就是進行JNI調用時,把一個Java中的String對象當數組來訪問了,最終導致虛擬機器崩潰。
先是看到這裡(http://code.google.com/p/android/issues/detail?id=12987)有人提到,Google的Nexus S因為使用了V8引擎所以不存在這個問題,就把我們系統中的JS引擎由JSC改為V8(具體過程參考這裡http://blog.csdn.net/a345017062/archive/2011/04/20/6336753.aspx),發現問題依然存在。沒辦法只能一步一步地跟蹤了。
對這個問題進行跟蹤時,費了很大的勁,所以先記錄一下跟蹤過程。
一開始,想著系統有自己的JNI調用跟蹤系統,就使用系統的吧(具體過程這裡有http://blog.csdn.net/a345017062/archive/2011/04/28/6369535.aspx),等我把編譯好的.so庫升級到板子上,查看LOG直接傻眼了,沒用的東西太多了,只能自己列印LOG了。
一共使用了兩個方法,一個是根據下面這段VM崩潰時系統列印的LOG中提到的拋出異常的本地方法,找出最終調用哪個函數時出現了問題。一開始沿著這個思路走,走得很艱難。因為JavaScriptCore中某些代碼無法使用LOGW等一系列方法,最後參考了一篇文章(這裡有詳細的描述http://blog.csdn.net/a345017062/archive/2011/05/04/6394897.aspx)才讓LOG出來
12-20 19:29:34.582: WARN/dalvikvm(725): JNI WARNING: jarray 0x4074f6e0 points to non-array object (Ljava/lang/String;)
12-20 19:29:34.622: INFO/dalvikvm(725): "WebViewCoreThread" prio=5 tid=9 NATIVE
12-20 19:29:34.622: INFO/dalvikvm(725): | group="main" sCount=0 dsCount=0 obj=0x40521328 self=0x26bea8
12-20 19:29:34.622: INFO/dalvikvm(725): | sysTid=734 nice=0 sched=0/0 cgrp=default handle=2539488
12-20 19:29:34.632: INFO/dalvikvm(725): | schedstat=( 5341953614 5808331660 581 )
12-20 19:29:34.632: INFO/dalvikvm(725): at android.webkit.WebViewCore.nativeTouchUp(Native Method)
12-20 19:29:34.632: INFO/dalvikvm(725): at android.webkit.WebViewCore.nativeTouchUp(Native Method)
12-20 19:29:34.642: INFO/dalvikvm(725): at android.webkit.WebViewCore.access$3300(WebViewCore.java:53)
12-20 19:29:34.652: INFO/dalvikvm(725): at android.webkit.WebViewCore$EventHub$1.handleMessage(WebViewCore.java:1158)
12-20 19:29:34.652: INFO/dalvikvm(725): at android.os.Handler.dispatchMessage(Handler.java:99)
12-20 19:29:34.652: INFO/dalvikvm(725): at android.os.Looper.loop(Looper.java:123)
12-20 19:29:34.652: INFO/dalvikvm(725): at android.webkit.WebViewCore$WebCoreThread.run(WebViewCore.java:629)
12-20 19:29:34.662: INFO/dalvikvm(725): at java.lang.Thread.run(Thread.java:1019)
12-20 19:29:34.672: ERROR/dalvikvm(725): VM aborting
另外一個方法就是利用addr2line工具,並結合下面這段LOG中給出的堆棧資訊找到具體出問題的方法。具體過程參考這篇文章
http://blog.chinaunix.net/space.php?uid=20564848&do=blog&id=94732
12-20 19:29:34.852: INFO/DEBUG(30): Build fingerprint: 'generic/sdk/generic:2.3/GRH55/79397:eng/test-keys'
12-20 19:29:34.862: INFO/DEBUG(30): pid: 725, tid: 734 >>> XXXXXXXX <<<
12-20 19:29:34.862: INFO/DEBUG(30): signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr deadd00d
12-20 19:29:34.872: INFO/DEBUG(30): r0 fffffebc r1 deadd00d r2 00000026 r3 00000000
12-20 19:29:34.872: INFO/DEBUG(30): r4 81da45c8 r5 4074f6e0 r6 81d8592c r7 0026d2b8
12-20 19:29:34.872: INFO/DEBUG(30): r8 44174b54 r9 43d72e4c 10 43d72e34 fp 41edd82c
12-20 19:29:34.891: INFO/DEBUG(30): ip 81da4728 sp 44174348 lr afd19375 pc 81d45a02 cpsr 20000030
12-20 19:29:35.912: INFO/DEBUG(30): #00 pc 00045a02 /system/lib/libdvm.so
12-20 19:29:35.912: INFO/DEBUG(30): #01 pc 000376fc /system/lib/libdvm.so
12-20 19:29:35.912: INFO/DEBUG(30): #02 pc 000399c4 /system/lib/libdvm.so
12-20 19:29:35.922: INFO/DEBUG(30): #03 pc 0003a4a0 /system/lib/libdvm.so
12-20 19:29:35.932: INFO/DEBUG(30): #04 pc 0032b6d6 /system/lib/libwebcore.so
12-20 19:29:35.952: INFO/DEBUG(30): #05 pc 002a4da4 /system/lib/libwebcore.so
12-20 19:29:35.952: INFO/DEBUG(30): #06 pc 001a6136 /system/lib/libwebcore.so
12-20 19:29:35.952: INFO/DEBUG(30): #07 pc 002a5870 /system/lib/libwebcore.so
12-20 19:29:35.962: INFO/DEBUG(30): #08 pc 00359e36 /system/lib/libwebcore.so
12-20 19:29:35.972: INFO/DEBUG(30): #09 pc 0035d18e /system/lib/libwebcore.so
12-20 19:29:35.972: INFO/DEBUG(30): #10 pc 0036b078 /system/lib/libwebcore.so
12-20 19:29:35.982: INFO/DEBUG(30): #11 pc 003627ac /system/lib/libwebcore.so
12-20 19:29:35.982: INFO/DEBUG(30): #12 pc 0029fb24 /system/lib/libwebcore.so
12-20 19:29:36.002: INFO/DEBUG(30): #13 pc 0023367e /system/lib/libwebcore.so
12-20 19:29:36.002: INFO/DEBUG(30): #14 pc 00239c0c /system/lib/libwebcore.so
12-20 19:29:36.011: INFO/DEBUG(30): #15 pc 00237a28 /system/lib/libwebcore.so
12-20 19:29:36.011: INFO/DEBUG(30): #16 pc 00237b32 /system/lib/libwebcore.so
12-20 19:29:36.022: INFO/DEBUG(30): #17 pc 0023adba /system/lib/libwebcore.so
12-20 19:29:36.022: INFO/DEBUG(30): #18 pc 0023af4e /system/lib/libwebcore.so
12-20 19:29:36.042: INFO/DEBUG(30): #19 pc 000f80c0 /system/lib/libwebcore.so
12-20 19:29:36.042: INFO/DEBUG(30): #20 pc 000f8c66 /system/lib/libwebcore.so
12-20 19:29:36.052: INFO/DEBUG(30): #21 pc 001b81fa /system/lib/libwebcore.so
12-20 19:29:36.062: INFO/DEBUG(30): #22 pc 001b82f2 /system/lib/libwebcore.so
12-20 19:29:36.062: INFO/DEBUG(30): #23 pc 001b8318 /system/lib/libwebcore.so
12-20 19:29:36.072: INFO/DEBUG(30): #24 pc 00017d34 /system/lib/libdvm.so
下面開始分析這個問題
先把這個地址(http://code.google.com/p/android/issues/detail?id=12987)中,樓主的例子下載下來,安裝在機器上運行。發現問題出在WebView.addJavascriptInterface方法中。只要使用Java擴充JS的API,並在指令碼中調用到這些API就會出現VM崩潰。
用上面提到的方法進行跟蹤,發現問題出現在
/external/webkit/WebCore/bridge/jni/jsc/JavaClassJSC.cpp中的這一句上:
if (jarray fields = (jarray)(callJNIMethod<jobject>(aClass, "getFields", "()[Ljava/lang/reflect/Field;"))){
int numFields = env->GetArrayLength(fields);
...
}
callJNIMethod<jobject>方法內部出現了錯誤,返回的是一個jstring,在這裡把它當jarray使用了。在調用env->GetArrayLength(fields)時,VM會使用/dalvik/vm/CheckJni.c中的checkArray方法對參數進行合法檢驗,一旦發現參數不是數組,就會調用abortMaybe()關閉VM。
現在問題到了(jarray)(callJNIMethod<jobject>(aClass, "getFields", "()[Ljava/lang/reflect/Field;"))為什麼會返回一個字串上了。
我在測試用的APK中使用System.out.println(t)對註冊擴充API時使用的Test對象進行列印,然後把callJNIMethod<jobject>返回的字串也進行列印,發現兩者相同。也就是說,無論給callJNIMethod<jobject>傳入什麼參數,返回結果都是序列化後的Test對象。
經過跟蹤,發現callJNIMethod最終會調用/dalvik/vm/interp/Stack.c中的方法dvmCallMethodV,我在dvmCallMethodV中列印了一下method->name,發現每次callJNIMethod調用的都是toString方法,這就能解釋為什麼callJNIMethod返回的字串是序列化後的Test對象的現象了。
看來,是callJNIMethod方法把繁瑣的JNI調用封裝在一起之後出現的問題。於是我自己寫了一個方法來替換callJNIMethod<jobject>:
jobject getReturnObject(JNIEnv* env, jobject anInstance, const char* name,
const char* signature) {
jclass testClass = env->GetObjectClass(anInstance);
if (!testClass) {
LOGW("error 1");
return NULL;
}
jmethodID methodID = env->GetMethodID(testClass, name, signature);
if (!methodID) {
LOGW("error 2");
return NULL;
}
jobject result = env->CallObjectMethod(anInstance, methodID);
if (!result) {
LOGW("error 3");
return NULL;
}
return result;
}
把使用callJNIMethod<jobject>的幾個地方都用getReturnObject方法進行替換,問題解決。
參考文章
在Android源碼的JavaScriptCore引擎中添加LOG
http://blog.csdn.net/a345017062/archive/2011/05/04/6394897.aspx
android或linux調試addr2line工具鎖定命令的使用
http://blog.chinaunix.net/space.php?uid=20564848&do=blog&id=94732