CHECK_STACK_SUM(mSelf);
dvmChangeStatus(mSelf, THREAD_RUNNING);
}
~ScopedJniThreadState() {
dvmChangeStatus(mSelf, THREAD_NATIVE);
COMPUTE_STACK_SUM(mSelf);
}
在使用dvmCallMethodV
调用 Java 方法前,会先切换状态为THREAD_RUNNING
,执行完毕后,ScopedJniThreadState
析构,再切换回THREAD_NATIVE
。这样,JNI 执行DexFile.loadDex
就和直接执行 Java 代码一样,状态会有问题。不只是CallStaticXXXMethod
,所有使用CallXXXMethod
函数在 Native 下调用 Java 方法的情况都是如此。
好在,我们想到了另一个办法:既然 Dalvik 不会对内部类的 JNI 调用做切换,我们就自己写一个 JNI 调用,使其走到 Native 代码中,这样线程就会变为 Native 状态,然后 直接调用虚拟机内部函数 做 dexopt 即可。这样在做 dexopt 的时候,始终会处于 NATIVE 的状态,不会切为 RUNNING,也不会被要求挂起,也就能避免这个问题。
这个虚拟机内部函数就是dvmRawDexFileOpen
,我们先来看下它的代码说明:
/*
-
Open a raw “.dex” file, optimize it, and load it.
-
On success, returns 0 and sets “*ppDexFile” to a newly-allocated DexFile.
-
On failure, returns a meaningful error code [currently just -1].
*/
int dvmRawDexFileOpen(const char* fileName, const char* odexOutputName,
RawDexFile** ppDexFile, bool isBootstrap);
这个函数可以用来打开原始 DEX 文件,并且对它做优化和加载。对应到 libdvm.so 中的符号是_Z17dvmRawDexFileOpenPKcS0_PP10RawDexFileb
,我们只需要用 dlsym 在 libdvm.so 里面找到它,就可以直接调用了,完整代码如下:
using func = int ()(constchar fileName, constchar* odexOutputName, void* ppRawDexFile, bool isBootstrap);
void* handler = dlopen(“libdvm.so”, RTLD_NOW);
dvmRawDexFileOpen = (func) dlsym(handler, “_Z17dvmRawDexFileOpenPKcS0_PP10RawDexFileb”);
dvmRawDexFileOpen(file_path, opt_file_path, &arg, false);
这样,我们自己写一个 JNI 调用,在 Native 状态下执行上述代码,就能达到完成 ODEX 的目的,从而根本上杜绝这个异常了