赋料扬雄敌,诗看子建亲。这篇文章主要讲述Android jni/ndk编程五:jni异常处理相关的知识,希望能为你提供帮助。
在Java的编程中,我们经常会遇到各种的异常,也会处理各种的异常。处理异常在java中非常简单,我们通常会使用try-catch-finally来处理,也可以使用throw简单抛出一个异常。那么在jni编程的时候我们又是如何处理异常的呢?
异常处理流程jni规范已经给我们做好了所有需要做的事情。回想一下处理异常的过程:
- 我们首先要在有可能产生异常的地方检测异常
- 处理异常
是的,我觉得异常的处理就是可以简单的总结为两步,在异常处理中我们通常会打印栈信息等。在jni编程中,异常处理的思路应该也与之类似,过程可用下图表示:
文章图片
在jni中我么需要手动清除异常。因此,jni中异常的处理应该严格遵循:检查-> 处理-> 清除的流程,在图中我把清除也认为是处理的其中一步了。
jint(*Throw)(JNIEnv*, jthrowable);
jint(*ThrowNew)(JNIEnv *, jclass, const char *);
jthrowable(*ExceptionOccurred)(JNIEnv*);
void(*ExceptionDescribe)(JNIEnv*);
void(*ExceptionClear)(JNIEnv*);
void(*FatalError)(JNIEnv*, const char*);
此外,还有一个函数和他们没放在一起:
jboolean(*ExceptionCheck)(JNIEnv*);
单从名字上我们可以知道用于检测异常的发生的函数有:
- (ExceptionCheck)(JNIEnv);
- (ExceptionOccurred)(JNIEnv);
用于清理异常的有: - (ExceptionClear)(JNIEnv);
用于跑出异常的有: - (Throw)(JNIEnv, jthrowable);
- (ThrowNew)(JNIEnv , jclass, const char *);
- (FatalError)(JNIEnv, const char*);
下面对以上函数做一个简单的介绍:
1> ExceptionCheck:检查是否发生了异常,若有异常返回JNI_TRUE,否则返回JNI_FALSE
2> ExceptionOccurred:检查是否发生了异常,若用异常返回该异常的引用,否则返回NULL
3> ExceptionDescribe:打印异常的堆栈信息
4> ExceptionClear:清除异常堆栈信息
5> ThrowNew:在当前线程触发一个异常,并自定义输出异常信息
6> Throw:丢弃一个现有的异常对象,在当前线程触发一个新的异常
7> FatalError:致命异常,用于输出一个异常信息,并终止当前VM实例(即退出程序)
native调用java中的方法,java中的方法抛出异常,我们在native中检测异常,检测到后抛出native中的异常,并清理异常。
c代码
void native_catchException(JNIEnv *env, jobject obj)
{
jthrowable exc;
jclass cls = (*env)->
GetObjectClass(env, obj);
jmethodID mid =(*env)->
GetMethodID(env, cls, "callbackException", "()V");
if (mid == NULL) {
return;
}
(*env)->
CallVoidMethod(env, obj, mid);
exc = (*env)->
ExceptionOccurred(env);
if (exc) {
jclass newExcCls;
(*env)->
ExceptionDescribe(env);
(*env)->
ExceptionClear(env);
newExcCls = (*env)->
FindClass(env,"java/lang/IllegalArgumentException");
if (newExcCls == NULL) {
/* Unable to find the exception class, give up. */
return;
}
(*env)->
ThrowNew(env, newExcCls, "thrown from C code");
}
}static JNINativeMethod gMethods[] = {
...
{"exception","()V",(void *)native_catchException},
};
代码的其他部分请参看之前的章节。
java代码
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = (TextView) findViewById(R.id.text);
try {
exception();
} catch (Exception e) {
System.out.println("In Java:\n\t" + e);
}
}
private native void exception() throws IllegalArgumentException;
private void callbackException() throws NullPointerException {
throw new NullPointerException("MainActivity.callbackException");
}
输出
09-26 10:58:50.012 23934-23934/com.jinwei.jnitesthello W/System.err: java.lang.NullPointerException: MainActivity.callbackException
09-26 10:58:50.012 23934-23934/com.jinwei.jnitesthello W/System.err:at com.jinwei.jnitesthello.MainActivity.callbackException(MainActivity.java:24)
09-26 10:58:50.012 23934-23934/com.jinwei.jnitesthello W/System.err:at com.jinwei.jnitesthello.MainActivity.exception(Native Method)
09-26 10:58:50.012 23934-23934/com.jinwei.jnitesthello W/System.err:at com.jinwei.jnitesthello.MainActivity.onCreate(MainActivity.java:32)
09-26 10:58:50.013 23934-23934/com.jinwei.jnitesthello W/System.err:at android.app.Activity.performCreate(Activity.java:6299)
09-26 10:58:50.013 23934-23934/com.jinwei.jnitesthello W/System.err:at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1107)
09-26 10:58:50.013 23934-23934/com.jinwei.jnitesthello W/System.err:at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2369)
09-26 10:58:50.013 23934-23934/com.jinwei.jnitesthello W/System.err:at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2476)
09-26 10:58:50.013 23934-23934/com.jinwei.jnitesthello W/System.err:at android.app.ActivityThread.-wrap11(ActivityThread.java)
09-26 10:58:50.013 23934-23934/com.jinwei.jnitesthello W/System.err:at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1344)
09-26 10:58:50.013 23934-23934/com.jinwei.jnitesthello W/System.err:at android.os.Handler.dispatchMessage(Handler.java:102)
09-26 10:58:50.013 23934-23934/com.jinwei.jnitesthello W/System.err:at android.os.Looper.loop(Looper.java:148)
09-26 10:58:50.013 23934-23934/com.jinwei.jnitesthello W/System.err:at android.app.ActivityThread.main(ActivityThread.java:5417)
09-26 10:58:50.013 23934-23934/com.jinwei.jnitesthello W/System.err:at java.lang.reflect.Method.invoke(Native Method)
09-26 10:58:50.013 23934-23934/com.jinwei.jnitesthello W/System.err:at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:731)
09-26 10:58:50.013 23934-23934/com.jinwei.jnitesthello W/System.err:at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:621)
09-26 10:58:50.013 23934-23934/com.jinwei.jnitesthello I/System.out: In Java:
09-26 10:58:50.013 23934-23934/com.jinwei.jnitesthello I/System.out:java.lang.IllegalArgumentException: thrown from C code
从输出中我们看到c代码中使用:(*env)-> ThrowNew(env, newExcCls, “thrown from C code”); 这行代码跑出了异常。然后java中的异常处理函数打印了栈的信息。
工具函数JNI中抛异常很经典:找异常类,调用ThrowNew抛出之;所以,可以写一个工具函数。
void
JNU_ThrowByName(JNIEnv *env, const char *name, const char *msg)
{
jclass cls = (*env)->
FindClass(env, name);
/* if cls is NULL, an exception has already been thrown */
if (cls != NULL) {
(*env)->
ThrowNew(env, cls, msg);
}
/* free the local ref */
(*env)->
DeleteLocalRef(env, cls);
}
推荐阅读
- 如何解决安卓手机键盘弹出将页面压缩
- Android 线程
- [开源项目] Android校验库 - FireEye
- 小程序美容美发营销系统app
- My app status is Ready for Sale but I cannot see my app on the App Store. Why? 为什么审核通过后 appstore中搜不到
- spring基于配置applicationContext.xml实现定时任务
- Android Studio若干实用的插件
- 类似Bootstrap的替代框架有哪些(合集介绍)
- React Native与Flutter差异(有什么区别())