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之缓存与引用

目录
相关文章
|
6月前
|
算法 编译器 C语言
【C++ 异常】C++ 标准库异常类及其应用
【C++ 异常】C++ 标准库异常类及其应用
77 0
|
6月前
|
Java UED
Java中的异常处理:捕获、声明与抛出
Java中的异常处理:捕获、声明与抛出
143 0
|
Java 编译器 开发者
java中运行时异常与编译时异常?
java中运行时异常与编译时异常?
|
6月前
|
C++
CPP的异常处理
CPP的异常处理
87 0
|
6月前
|
安全 Java 程序员
【Java 异常处理】异常处理机制,内置异常类,如何捕获异常
【Java 异常处理】异常处理机制,内置异常类,如何捕获异常
|
存储 缓存 编解码
JNI之常见技巧与陷阱
NDK/JNI连载系列
122 0
|
Java
JNI学习(3)——运行基于JNI的java程序
JNI学习(3)——运行基于JNI的java程序
97 0
JNI学习(3)——运行基于JNI的java程序
|
Java Unix Linux
JNI学习(0)——关于JNI
JNI学习(0)——关于JNI
141 0
JNI学习(0)——关于JNI
JNI中访问JList的代码
JNI中访问JList的代码
63 0