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 学习路线指南,也可关注我的微信公众号,我会在公众号上持续分享我的经验,帮助正在学习的你少走一些弯路。学习过程中如果你有疑问或者你的经验想要分享给大家可以添加我的微信,我拉你进技术交流群。
© 版权声明
文章版权归作者所有,未经允许请勿转载,侵权请联系 admin@trc20.tw 删除。
THE END