JNI异常处理
来源:互联网 发布:应用系统性能优化方案 编辑:程序博客网 时间:2024/05/17 17:16
本地代码中如何缓存和抛出异常
根据一个例子来介绍:
1.新建一个CatchThrow.java
public class CatchThrow { public native void doit() throws IllegalArgumentException; private void callback() throws NullPointerException{ throw new NullPointerException("CatchThrow.callback"); }}
2.JNI实现
JNIEXPORT void JNICALL Java_com_test_git_jnidemo_JniUtil_CatchThrow_doit (JNIEnv *env, jobject jobj){ jthrowable exc; //获取jclass jclass cls = (*env).GetObjectClass(jobj); //获取callback 方法id jmethodID mid = (*env).GetMethodID(cls, "callback", "()V"); if(mid == NULL){ return; } //调用callback方法 (*env).CallVoidMethod(jobj, mid); //异常检查 exc = (*env).ExceptionOccurred(); if(exc){ //异常处理 jclass newExcCls; //输出异常 (*env).ExceptionDescribe(); //清除异常 (*env).ExceptionClear(); newExcCls = (*env).FindClass("java/lang/IllegalArgumentException"); if(newExcCls == NULL){ //没有发现异常 return; } (*env).ThrowNew(newExcCls, "thrown from C code"); }};
3.Java中调用
CatchThrow catchThrow = new CatchThrow(); try { catchThrow.doit(); }catch (Exception e){ Log.i(TAG, "In Java catchThrow: " + e); }
4.输出结果:
09-29 17:16:16.291 18958-18958/com.test.git.jnidemo W/System.err: java.lang.NullPointerException: CatchThrow.callback09-29 17:16:16.291 18958-18958/com.test.git.jnidemo W/System.err: at com.test.git.jnidemo.JniUtil.CatchThrow.callback(CatchThrow.java:11)09-29 17:16:16.291 18958-18958/com.test.git.jnidemo W/System.err: at com.test.git.jnidemo.JniUtil.CatchThrow.doit(Native Method)09-29 17:16:16.291 18958-18958/com.test.git.jnidemo W/System.err: at com.test.git.jnidemo.Activity.MainActivity.catchThrow(MainActivity.java:26)09-29 17:16:16.291 18958-18958/com.test.git.jnidemo W/System.err: at com.test.git.jnidemo.Activity.MainActivity.onCreate(MainActivity.java:19)......09-29 17:16:16.301 18958-18958/com.test.git.jnidemo I/MainActivity-: In Java catchThrow: java.lang.IllegalArgumentException: thrown from C code
制作一个抛出异常的工具函数
抛出一个异常通常需要两步:
1.通过FindClass找到异常类
2.调用ThrowNew函数生成异常。
void JUN_ThrowByName(JNIEnv *env, const char *name, const char *msg){ //生成jclass jclass cls = (*env).FindClass(name); //如果cls为NULL,一个异常已经发生 if(cls != NULL){ (*env).ThrowNew(cls, msg); } //释放本地引用 (*env).DeleteLocalRef(cls);};
JNU_ThrowByName 这个工具函数首先使用 FindClass 函数来找到异常类,如果 FindClass 执行失败(返回 NULL),VM 会抛出一个异常(比如 NowClassDefFoundError),这种情况下 JNI_ThrowByName 不会再抛出另外一个
异常。如果 FindClass 执行成功的话,我们就通过 ThrowNew 来抛出一个指定名 字的异常。当函数 JNU_ThrowByName 返回时,它会保证有一个异常需要处理,但 这个异常不一定是 name 参数指定的异常。当函数返回时,记得要删除指向异常 类的局部引用。向 DeleteLocalRef 传递 NULL 不会产生作用。
异常处理
异常检查
检查异常有两种方式:
1.大部分JNI函数会通过特定的返回值(NULL)来表示已经发生了一个错误,并且当前线程中又一个异常需要处理。在C语言中,用返回值来标识错误信息是一个很常见的方式。
jclass localRefCls = (*env).FindClass("java/lang/String"); if(localRefCls == NULL){ return; //exeption }
2.当一个JNI函数返回一个明确的错误码时,可以用ExceptionCheck来检查是否有异常发生。但是,用返回的错误码来判断比较高效。一旦JNI函数的返回值是一个错误码,那么接下来调用ExceptionCheck肯定会返回JNI_TRUE。
//异常检查 exc = (*env).ExceptionOccurred(); jboolean error = (*env).ExceptionCheck(); if(error){ //异常处理 jclass newExcCls; //输出异常 (*env).ExceptionDescribe(); //清除异常 (*env).ExceptionClear(); newExcCls = (*env).FindClass("java/lang/IllegalArgumentException"); if(newExcCls == NULL){ //没有发现异常 return; } (*env).ThrowNew(newExcCls, "thrown from C code"); }
异常处理
本地代码通常有两种方式来处理一个异常:
1.一旦发生异常,立即返回,让调用者处理这个异常。
2.通过ExceptionClear清除异常,然后执行自己的异常处理代码。
当一个异常发生后,必须先检查、处理、清除异常后再做其他JNI函数调用,否则的话,结果未知。
通常来说,当有一个未处理的异常时,你只可以调用两种JNI函数:异常处理和清除VM资源的函数。
当异常发生时,释放资源是一件很重要的事。比如:
JNIEXPORT void JNICALL Java_pkg_Cls_f(JNIEnv *env, jclass cls, jstring jstr) { const jchar *cstr = (*env)->GetStringChars(env, jstr); if (c_str == NULL) { return; } ... if (...) { /* exception occurred */ (*env)->ReleaseStringChars(env, jstr, cstr); return; } ... /* normal return */ (*env)->ReleaseStringChars(env, jstr, cstr);}
工具函数中的异常
编写工具函数时,要把工具函数内部分发生的异常传播到调用它的方法中。
这里有两个方法:
1.对调用者来说,工具函数提供一个错误返回码比简单地把异常传播过去更方便一些。
2.工具函数在发生异常时尤其需要注意管理局部引用的方式。
jvalue JUN_CallMethodByName(JNIEnv *env, jboolean *hasException, jobject jobj, const char* name, const char* descriptor, ...){ va_list args; jclass clazz; jmethodID mid; jvalue result; if((*env).EnsureLocalCapacity(2) == JNI_OK){ clazz = (*env).GetObjectClass(jobj); mid = (*env).GetMethodID(clazz, name, descriptor); if(mid){ const char *p = descriptor; while (*p != ')') p++; p++; va_start(args, descriptor); switch (*p){ case 'V': (*env).CallVoidMethodV(jobj, mid, args); break; case '[': case 'L': result.l = (*env).CallObjectMethodV(jobj, mid, args); break; case 'Z': result.z = (*env).CallBooleanMethodV(jobj, mid, args); break; default: (*env).FatalError("illegal descriptor"); break; } va_end(args); } (*env).DeleteLocalRef(clazz); } if(hasException){ *hasException = (*env).ExceptionCheck(); } return result;};
JNU_CallMethodByName 的参数当中有一个 jboolean 指针,如果函数执行成功的 话,指针指向的值会被设置为 JNI_TRUE,如果有异常发生的话,会被设置成 JNI_FALSE。这就可以让调用者方便地检查异常。
JNU_CallMethodByName 首先通过 EnsureLocalCapacity 来确保可以创建两个局 部引用,一个类引用,一个返回值。接下来,它从对象中获取类引用并查找方法 ID。根据返回类型,switch 语句调用相应的 JNI 方法调用函数。回调过程完成 后,如果 hasException 不是 NULL,我们调用 ExceptionCheck 检查异常。 函数 ExceptionCheck 和 ExceptionOccurred 非常相似,不同的地方是,当有异 常发生时,ExceptionCheck 不会返回一个指向异常对象的引用,而是返回 JNI_TRUE,没有异常时,返回 JNI_FALSE。而 ExceptionCheck 这个函数不会返 回一个指向异常对象的引用,它只简单地告诉 地代码是否有异常发生。上面的 代码如果使用 ExceptionOccurred 的话,应该这么写:
if(hasException){// *hasException = (*env).ExceptionCheck(); jthrowable exc = (*env).ExceptionOccurred(); *hasException = exc != NULL; (*env).DeleteLocalRef(exc); }
为了删除指向异常对象的局部引用,DeleteLocalRef 方法必须被调用。 使用 JNU_CallMethodByName 这个工具函数,我们可以重写 Instance-MethodCall.nativeMethod 方法的实现:
JNIEXPORT void JNICALL Java_InstanceMethodCall_nativeMethod(JNIEnv *env, jobject obj) { printf("In C\n"); JNU_CallMethodByName(env, NULL, obj, "callback", "()V"); }
调用 JNU_CallMethodByName 函数后,我们不需要检查异常,因为 地方法后面 会立即返回。
- JNI异常的处理
- JNI异常的处理
- JNI异常的处理
- jni的异常处理
- JNI异常处理
- JNI异常处理
- JNI异常处理
- JNI异常处理
- JNI异常处理
- NDK/JNI异常处理
- JNI异常处理
- JNI异常处理
- Android JNI抛出异常处理
- JNI由浅入深_9_JNI 异常处理
- JNI数组传递与异常处理
- JNI数组传递与异常处理
- JNI数组传递与异常处理
- JNI数组传递与异常处理
- 经典数据算法试题
- thrift C++代码分析
- imageloader+gridview
- Mac 下 Chrome浏览器 50版本无法跨域的解决
- JTable如何像excel一样固定行列,不随滚动条移动
- JNI异常处理
- 全球同服架构设计
- 获取验证码倒计时
- 从TS流到PAT和PMT
- Report Studio 汇总
- 创建第一个vue应用
- MongoDB高级部分及实现(一)
- css+div布局,版心的使用(一)拉伸不会改变布局
- C# 队列处理日志