JNI异常处理

简介: NDK/JNI连载系列

前言

本文所要介绍的异常处理是指通过JNI调用java层方法时产生的异常处理,并不是指JNI调用Native层函数时产生的异常处理,如果童鞋们想要了解Native层的异常处理可以参考笔者之前的文章《C++之异常处理》

按照java的经验,当发生异常而又没有捕获时,异常后面的代码就得不到继续执行的机会,但是在JNI中不同,在JNI中如果调用java层的方法抛出了异常,依然会继续往后执行,但是这些行为往往会带来各种各样的"惊喜"。。。因此我们需要在异常发生时将这些异常及时进行处理。

本文主要从捕获java层异常、向java层抛出异常两个方面介绍JNI中的异常处理机制。

捕获java层异常

当Native调用java层发生异常时可以通过函数ExceptionOccurred检测是否有发生异常,通过函数ExceptionDescribe输出异常描述信息,如果检测到异常处理完毕或者不进行异常处理又想让程序继续往下执行,那么通过函数ExceptionClear将异常清除即可。

检测是否有异常时还可以使用函数ExceptionCheck,它与ExceptionOccurred函数的作用类似,不同之处在于ExceptionCheck函数不会返回异常对象的引用,而是返回一个异常是否发生的jboolean标识,当返回的标识为JNI_TRUE时代表有异常发生。当调用者不关心异常的类型,仅仅关心是否发生了异常的时候,使用它会更加方便而且高效。

向java层抛出异常

当希望将捕获到的异常向java层继续抛出时,可以通过JNI函数ThrowNew抛出一个异常,这个异常可以被java层的try catch语句块捕获到。

以下是一个在Native层捕获到一个除数为0的异常,并将其抛出给java层的demo:

NumUtils.java

public class NumUtils {
    public void div(int a,int b){
        if(b == 0){
            throw new IllegalArgumentException("除数不能为0");
        }
        int c = a/b;
    }

    // native函数
    public native void callByJni();
}

MainActivity.java

public class MainActivity extends AppCompatActivity {
    static {
        System.loadLibrary("jnitest");
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        TextView textView = findViewById(R.id.sample_text);
        textView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                NumUtils numUtils = new NumUtils();
                try {
                    numUtils.callByJni();
                }catch (Exception e){
                    Log.v("NumUtils","捕获到的异常:" + e.getMessage());
                }
            }
        });
    }

}

native-lib.cpp

extern "C"
JNIEXPORT void JNICALL
Java_com_fly_jnitest_NumUtils_callByJni(JNIEnv *env, jobject thiz) {
    jclass clazz = env->GetObjectClass(thiz);
    jmethodID mid = env->GetMethodID(clazz,"div","(II)V");
    env->CallVoidMethod(thiz,mid,-1,0);
    jboolean hasException = env->ExceptionCheck();
    if(hasException == JNI_TRUE){
        __android_log_print(ANDROID_LOG_DEBUG,"NumUtils","调用java层方法有异常");
        // 清除异常,不让崩溃
        env->ExceptionClear();
        // 抛出异常
        jclass exceptionClazz = env->FindClass("java/lang/IllegalArgumentException");
        env->ThrowNew(exceptionClazz,"native层捕获到异常,向java层抛出");
        env->DeleteLocalRef(exceptionClazz);
    }
}

小结

1、为了更好地在Native层处理好异常,一般建议在调用CallVoidMethod等一系列函数之后进行异常检测。
2、在设计供Native层调用的函数时尽可能地带上返回值,方便Native层按照返回值判断是否需要处理异常等。
3、当捕获到异常需要提前return程序时需要保证各种资源及时释放。
4、为了简化抛出异常的方法《JNI编程指南》一书中给出了一个工具函数:

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){
    (*env)->ThrowNew(env, cls, msg);
  }
  /* free the local ref */
  (*env)->DeleteLocalRef(env, cls);
}

系列推荐

JNI基础简介
JNI之数组与字符串的使用
JNI之动态注册与静态注册
JNI之访问java属性和方法
JNI之缓存与引用

目录
相关文章
|
2月前
|
Java UED
Java中的异常处理:捕获、声明与抛出
Java中的异常处理:捕获、声明与抛出
78 0
|
2月前
|
C++
CPP的异常处理
CPP的异常处理
47 0
|
2月前
|
安全 Java 程序员
【Java 异常处理】异常处理机制,内置异常类,如何捕获异常
【Java 异常处理】异常处理机制,内置异常类,如何捕获异常
|
Java Android开发 C++
Android C++系列:JNI调用时的异常处理
Native 提供了 ExceptionOccurred 和 ExceptionCheck 方法来检测是否有异常发生,前者返回的是 jthrowable 类型,后者返回的是 jboolean 类型。
178 0
|
存储 缓存 编解码
JNI之常见技巧与陷阱
NDK/JNI连载系列
89 0
JNI中访问JList的代码
JNI中访问JList的代码
56 0
|
Java Unix Linux
JNI学习(0)——关于JNI
JNI学习(0)——关于JNI
121 0
JNI学习(0)——关于JNI
|
Java
JNI学习(3)——运行基于JNI的java程序
JNI学习(3)——运行基于JNI的java程序
83 0
JNI学习(3)——运行基于JNI的java程序
JNI FindClass出错的一种特殊情况
JNI FindClass出错的一种特殊情况
101 0

热门文章

最新文章

  • 1
    流量控制系统,用正则表达式提取汉字
    25
  • 2
    Redis09-----List类型,有序,元素可以重复,插入和删除快,查询速度一般,一般保存一些有顺序的数据,如朋友圈点赞列表,评论列表等,LPUSH user 1 2 3可以一个一个推
    26
  • 3
    Redis08命令-Hash类型,也叫散列,其中value是一个无序字典,类似于java的HashMap结构,Hash结构可以将对象中的每个字段独立存储,可以针对每字段做CRUD
    26
  • 4
    Redis07命令-String类型字符串,不管是哪种格式,底层都是字节数组形式存储的,最大空间不超过512m,SET添加,MSET批量添加,INCRBY age 2可以,MSET,INCRSETEX
    27
  • 5
    S外部函数可以访问函数内部的变量的闭包-闭包最简单的用不了,闭包是内层函数+外层函数的变量,简称为函数套函数,外部函数可以访问函数内部的变量,存在函数套函数
    24
  • 6
    Redis06-Redis常用的命令,模糊的搜索查询往往会对服务器产生很大的压力,MSET k1 v1 k2 v2 k3 v3 添加,DEL是删除的意思,EXISTS age 可以用来查询是否有存在1
    30
  • 7
    Redis05数据结构介绍,数据结构介绍,官方网站中看到
    22
  • 8
    JS字符串数据类型转换,字符串如何转成变量,+号只要有一个是字符串,就会把另外一个转成字符串,- * / 都会把数据转成数字类型,数字型控制台是蓝色,字符型控制台是黑色,
    20
  • 9
    JS数组操作---删除,arr.pop()方法从数组中删除最后一个元素,并返回该元素的值,arr.shift() 删除第一个值,arr.splice()方法,删除指定元素,arr.splice,从第一
    20
  • 10
    定义好变量,${age}模版字符串,对象可以放null,检验数据类型console.log(typeof str)
    19