下面开始分析这个问题
先把这个地址中,楼主的例子下载下来,安装在机器上运行。发现问题出在WebView.addJavascriptInterface方法中。只要使用Java扩展JS的API,并在脚本中调用到这些API就会出现VM崩溃。
用上面提到的方法进行跟踪,发现问题出现在
/external/webkit/WebCore/bridge/jni/jsc/JavaClassJSC.cpp中的这一句上:
if (jarray fields = (jarray)(callJNIMethod(aClass, "getFields", "()[Ljava/lang/reflect/Field;"))){
int numFields = env->GetArrayLength(fields);
...
}
callJNIMethod方法内部出现了错误,返回的是一个jstring,在这里把它当jarray使用了。在调用env->GetArrayLength(fields)时,VM会使用/dalvik/vm/CheckJni.c中的checkArray方法对参数进行合法检验,一旦发现参数不是数组,就会调用abortMaybe()关闭VM。
现在问题到了(jarray)(callJNIMethod(aClass, "getFields", "()[Ljava/lang/reflect/Field;"))为什么会返回一个字符串上了。
我在测试用的APK中使用System.out.println(t)对注册扩展API时使用的Test对象进行打印,然后把callJNIMethod返回的字符串也进行打印,发现两者相同。也就是说,无论给callJNIMethod传入什么参数,返回结果都是序列化后的Test对象。
经过跟踪,发现callJNIMethod最终会调用/dalvik/vm/interp/Stack.c中的方法dvmCallMethodV,我在dvmCallMethodV中打印了一下method->name,发现每次callJNIMethod调用的都是toString方法,这就能解释为什么callJNIMethod返回的字符串是序列化后的Test对象的现象了。
看来,是callJNIMethod方法把繁琐的JNI调用封装在一起之后出现的问题。于是我自己写了一个方法来替换callJNIMethod:
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的几个地方都用getReturnObject方法进行替换,问题解决。
参考文章
在Android源码的JavaScriptCore引擎中添加LOG
android或linux调试addr2line工具锁定命令的使用