JNI 程序中的反常分为以下几种:

  • Native 程序原生反常,一般经过函数回来值和 linux 信号处理, C++ 中也有 try catch 机制解决反常,不是本文重点
  • JNIEnv 内部函数抛出的反常,一般经过回来值判断,发现反常直接 return, jvm 会给将反常传递给 Java
  • Native 回调 Java 层办法,被回调的办法抛出反常,JNI 提供了特定的 API 来处理这类反常

1. JNIEnv 内部函数抛出的反常

很多 JNIEnv 中的函数都会抛出反常,处理办法大体上是一致的:

  • 回来值与特别值(一般是 NULL)比较,知晓函数是否产生反常
  • 假如产生反常当即 return
  • jvm 会将反常抛给 java 层,咱们能够在 java 层经过 try catch 机制捕获反常

接着咱们来看一个比如:

Java 层:

public native void exceptionTest();
//调用
try {
     exceptionTest();
} catch (Exception e) {
    e.printStackTrace();
}

Native 层:

extern "C"
JNIEXPORT void JNICALL
Java_com_yuandaima_myjnidemo_MainActivity_exceptionTest(JNIEnv *env, jobject thiz) {   
    //查找的类不存在,回来 NULL;
    jclass clazz = env->FindClass("com/yuandaima/myjnidemo/xxx");
    if (clazz == NULL) {
        return; //return 后,jvm 会向 java 层抛出 ClassNotFoundException
    }
}

执行后的 log:

java.lang.ClassNotFoundException: Didn't find class "com.yuandaima.myjnidemo.xxx"

说明,java 层捕获到了反常

2. Native 回调 Java 层办法,被回调的办法抛出反常

Native 回调 Java 层办法,被回调的办法抛出反常。这样情况下一般有两种解决办法:

  • Java 层 Try catch 本地办法,这是比较引荐的办法。
  • Native 层处理反常,反常处理假如和 native 层相关,能够选用这种办法

2.1 Java 层 Try catch 本地办法

Java 层:

//执行这个办法会抛出反常
private static int exceptionMethod() {
    return 20 / 0;
}
//native 办法,在 native 中,会调用到 exceptionMethod() 办法
public native void exceptionTest();
//Java 层调用
try {
    exceptionTest();
} catch (Exception e) {
    //这儿处理反常
    //一般是打 log 和弹 toast 告诉用户
    e.printStackTrace();
}

Native 层:

extern "C"
JNIEXPORT void JNICALL
Java_com_yuandaima_myjnidemo_MainActivity_exceptionTest(JNIEnv *env, jobject thiz) {
    jclass clazz = env->FindClass("com/yuandaima/myjnidemo/TestJavaClass");
    if (clazz == NULL) {
        return;
    }
    //调用 java 层会抛出反常的办法
    jmethodID static_method_id = env->GetStaticMethodID(clazz, "exceptionMethod", "()I");
    if (NULL == static_method_id) {
        return;
    }
    //直接调用,产生 ArithmeticException 反常,传回 Java 层
    env->CallStaticIntMethod(clazz, static_method_id);
    env->DeleteLocalRef(clazz);
}

2.2 Native 层处理反常

有的反常需要在 Native 处理,这儿又分为两类:

  • 反常在 Native 层就处理完了
  • 反常在 Native 层处理了,还需要回来给 Java 层,Java 层继续处理

接着咱们看下示例:

Java 层:

//执行这个办法会抛出反常
private static int exceptionMethod() {
    return 20 / 0;
}
//native 办法,在 native 中,会调用到 exceptionMethod() 办法
public native void exceptionTest();
//Java 层调用
try {
    exceptionTest();
} catch (Exception e) {
    //这儿处理反常
    //一般是打 log 和弹 toast 告诉用户
    e.printStackTrace();
}

Native 层:

extern "C"
JNIEXPORT void JNICALL
Java_com_yuandaima_myjnidemo_MainActivity_exceptionTest(JNIEnv *env, jobject thiz) {
    jthrowable mThrowable;
    jclass clazz = env->FindClass("com/yuandaima/myjnidemo/TestJavaClass");
    if (clazz == NULL) {
        return;
    }
    jmethodID static_method_id = env->GetStaticMethodID(clazz, "exceptionMethod", "()I");
    if (NULL == static_method_id) {
        return;
    }
    env->CallStaticIntMethod(clazz, static_method_id);
    //检测是否有反常产生
    if (env->ExceptionCheck()) {
        //获取到反常对象
        mThrowable = env->ExceptionOccurred();
        //这儿就能够依据实际情况处理反常了
        //.......
        //打印反常信息仓库
        env->ExceptionDescribe();
        //清除反常信息
        //假如,反常还需要 Java 层处理,能够不调用 ExceptionClear,让反常传递给 Java 层
        env->ExceptionClear();
        //假如调用了 ExceptionClear 后,反常还需要 Java 层处理,咱们能够抛出一个新的反常给 Java 层
        jclass clazz_exception = env->FindClass("java/lang/Exception");
        env->ThrowNew(clazz_exception, "JNI抛出的反常!");
        env->DeleteLocalRef(clazz_exception);
    }
    env->DeleteLocalRef(clazz);
    env->DeleteLocalRef(mThrowable);
}

关于

我叫阿豪,2015 年本科结业于国防科技大学指挥自动化专业,结业后,从事信息化配备的研制作业。首要研讨方向为 Android Framework 与 Linux Kernel,2023年春节后开端做 Android Framework 相关的技能共享。

假如你对 Framework 感兴趣或许正在学习 Framework,能够参考我总结的Android Framework 学习路线指南,也可关注我的微信大众号,我会在大众号上持续共享我的经验,帮助正在学习的你少走一些弯路。学习过程中假如你有疑问或许你的经验想要共享给我们能够增加我的微信,我拉你进技能交流群。

JNI 编程上手指南之异常处理