Java JNI打印Java层异常

在一些复杂的JNI调用中,比如JNI调用Java层的对象、Java层又调native方法,嵌套过多了,某一次调用产生的异常会在下一次调用JNI时被check出来,
这时候会产生如下日志:

01-13 21:22:43.247 24613-24613/com.somepkg A/art: art/runtime/check_jni.cc:65] JNI DETECTED ERROR IN APPLICATION: JNI NewByteArray called with pending exception 'java.lang.NullPointerException' thrown in unknown throw location
01-13 21:22:43.247 24613-24613/com.somepkg A/art: art/runtime/check_jni.cc:65] in call to NewByteArray

这种问题都是在Java代码中产生了异常,但是并不是所有case下都能一眼通过逻辑判断是哪行,这时候有个JNIEnv的方法能帮上我们大忙:

jthrowable thr = (*env)->ExceptionOccurred(env);
if (thr) {
(*env)->ExceptionDescribe(env);
(*env)->ExceptionClear(env);
}

c++版本:

void CheckException(JNIEnv* env) {
if (!HasException(env)) return;
// Exception has been found, might as well tell breakpad about it.
jthrowable java_throwable = env->ExceptionOccurred();
if (!java_throwable) {
// Do nothing but return false.
CHECK(false);
}
// Clear the pending exception, since a local reference is now held.
env->ExceptionDescribe();
env->ExceptionClear();
// Set the exception_string in BuildInfo so that breakpad can read it.
// RVO should avoid any extra copies of the exception string.
base::android::BuildInfo::GetInstance()->set_java_exception_info(
GetJavaExceptionInfo(env, java_throwable));
// Now, feel good about it and die.
CHECK(false);
}

在被checkjni侦测到异常的代码(比如上例中是NewByteArray)之前加上如上代码就可以将Java层的异常信息给优雅地打印出来,从而精准定位问题。

另一种定位此类问题的方式是打开Android设备的CheckJni功能。但是一般production设备上都不太容易实现。所以强力推荐以上方法。