JNI Crash:异常定位与捕获处理

crash

在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 logcat
        env->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抛出的异常就可以在Java层通过Try…Catch捕获,并进行相应的出错提示,Java层代码如下:

public static int callJniDivide(int input1, int input2) {
   
        try 
  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
以下是 JNI 中遍历 std::map 的示例代码: ```c++ JNIEXPORT void JNICALL Java_com_example_MapExample_mapIteration(JNIEnv *env, jobject obj, jobject mapObj) { // 获取 Map 的 class 对象 jclass mapClass = env->GetObjectClass(mapObj); // 获取 Map.entrySet() 方法的 ID jmethodID entrySetMethodID = env->GetMethodID(mapClass, "entrySet", "()Ljava/util/Set;"); // 调用 Map.entrySet() 方法获取所有的键值对 jobject entrySetObj = env->CallObjectMethod(mapObj, entrySetMethodID); // 获取 Set 的 class 对象 jclass setClass = env->GetObjectClass(entrySetObj); // 获取 Set.iterator() 方法的 ID jmethodID iteratorMethodID = env->GetMethodID(setClass, "iterator", "()Ljava/util/Iterator;"); // 调用 Set.iterator() 方法获取迭代器 jobject iteratorObj = env->CallObjectMethod(entrySetObj, iteratorMethodID); // 获取 Iterator 的 class 对象 jclass iteratorClass = env->GetObjectClass(iteratorObj); // 获取 Iterator.hasNext() 方法的 ID jmethodID hasNextMethodID = env->GetMethodID(iteratorClass, "hasNext", "()Z"); // 获取 Iterator.next() 方法的 ID jmethodID nextMethodID = env->GetMethodID(iteratorClass, "next", "()Ljava/lang/Object;"); // 遍历 Map 中的所有键值对 while (env->CallBooleanMethod(iteratorObj, hasNextMethodID)) { // 调用 Iterator.next() 方法获取下一个键值对 jobject entryObj = env->CallObjectMethod(iteratorObj, nextMethodID); // 获取 Map.Entry 的 class 对象 jclass entryClass = env->GetObjectClass(entryObj); // 获取 Map.Entry.getKey() 方法的 ID jmethodID getKeyMethodID = env->GetMethodID(entryClass, "getKey", "()Ljava/lang/Object;"); // 获取 Map.Entry.getValue() 方法的 ID jmethodID getValueMethodID = env->GetMethodID(entryClass, "getValue", "()Ljava/lang/Object;"); // 调用 Map.Entry.getKey() 方法获取键 jstring keyObj = (jstring) env->CallObjectMethod(entryObj, getKeyMethodID); // 调用 Map.Entry.getValue() 方法获取值 jstring valueObj = (jstring) env->CallObjectMethod(entryObj, getValueMethodID); // 将键值转换为 C++ 字符串 const char* key = env->GetStringUTFChars(keyObj, NULL); const char* value = env->GetStringUTFChars(valueObj, NULL); // 输出键值对 printf("%s: %s\n", key, value); // 释放字符串所占用的内存 env->ReleaseStringUTFChars(keyObj, key); env->ReleaseStringUTFChars(valueObj, value); } } ``` 这段代码首先获取了 Map 对象的 class 对象,然后获取了 Map.entrySet() 方法的 ID,并调用该方法获取所有的键值对。接着获取了 Set 对象的 class 对象,以及 Set.iterator() 方法的 ID,并调用该方法获取迭代器。然后获取了 Iterator 对象的 class 对象,以及 Iterator.hasNext() 方法和 Iterator.next() 方法的 ID。最后通过迭代器遍历 Map 中的所有键值对,并获取每个键值对的键和值,将其转换为 C++ 字符串后输出。需要注意的是,遍历结束后需要释放字符串所占用的内存。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值