JNI Crash:异常定位与捕获处理
- 方法一:ExceptionCheck机制
- 方法二:信号量捕获机制
-
- 基础知识一:信号量机制
-
- signal函数使用
- sigaction的使用
- 基础知识二:Non-Local Exits
- 利用上面的两个知识点通过信号量进行Android jni崩溃捕获和处理
在Android JNI开发中,经常会遇到JNI崩溃的问题,尤其带代码量大,或者嵌入了第三方代码的情况下,很难进行问题定位和处理。本文将介绍两种常见的JNI崩溃处理方法,包括:
1. 每个JNI调用后进行异常检测处理(适用于JNI代码量很小的情况)
2. 捕获系统崩溃的Signal,并进行异常处理(适用于JNI代码量大,难以每句话后面都进行异常检测的情况)
本文github源码地址
下面将分别介绍两种方法:
方法一:ExceptionCheck机制
首先需要理解的是,JNI没有try…catch…finally机制,不能利用这种方法将整段的代码进行异常捕获。
在JNI调用中,如果发生异常,程序并不会停止执行,而是继续执行下一句代码,直到崩溃发生。正确的处理方法是在每一句JNI调用后面都通过ExceptionCheck函数手动检测是否发生了异常,如果检测到异常,进行异常处理。如下:
JNIEXPORT jint JNICALL Java_jack_com_jniexceptionhandler_Calculate_jniDivide(JNIEnv * env, jobject jobj, jint m, jint n) {
char* a = NULL;int val1 = a[1] - '0';// 每句jni执行之后都加入异常检查if (checkExc(env)) {
LOGE("jni exception happened at p0");JNU_ThrowByName(env, "java/lang/Exception", "exception from jni: jni exception happened at p0");return -1;}char* b = NULL;int val2 = b[1] - '0';// 每句jni执行之后都加入异常检查if (checkExc(env)) {
LOGE("jni exception happened at p1");JNU_ThrowByName(env, "java/lang/Exception", "exception from jni: jni exception happened at p1");return -1;}return val1/val2;
}
这里在每次JNI调用之后都要检测是否发生了异常,检测函数checkExec实现如下:
int checkExc(JNIEnv *env) {
if(env->ExceptionCheck()) {
env->ExceptionDescribe(); // writes to logcatenv->ExceptionClear();return 1;}return -1;
}
如果检测到异常,可以在JNI层将异常抛出到Java层进行处理,JNI代码如下:
void JNU_ThrowByName(JNIEnv *env, const char *name, const char *msg)
{
// 查找异常类jclass cls = env->FindClass(name);/* 如果这个异常类没有找到,VM会抛出一个NowClassDefFoundError异常 */if (cls != NULL) {
env->ThrowNew(cls, msg); // 抛出指定名字的异常}/* 释放局部引用 */env->DeleteLocalRef(cls);}
这样,JNI抛出的异常就