Android NDK开发(十二):JNI函数接口详解—异常处理

Android NDK开发(十二):JNI函数接口详解—异常处理,第1张

1 JNI层的异常 和 JNI使用的异常

       由于JNI层既涉及到Java层也涉及到C/C++层,故在JNI层,可以抛出C/C++语言定义的异常、也可以抛出java语言定义的异常,哎?等会,是不是还可以抛出JNI定义的异常?
       不会,因为JNI标准没有定义自己的异常,JNI使用的都是java定义的异常,所以在JNI层只可能有C/C++定义的异常 和 java定义的异常,后面我们简称C/C++异常 和 java异常。那么在JNI层上述两种异常有哪些来源呢?请看下图:

        从上图可知,在JNI层,JNI接口 或 通过JNI接口调用的java方法可以抛出java异常,C/C++代码可以抛出C/C++异常,其中通过JNI接口调用的java方法抛出的java异常,是如何从java层抛到JNI层的呢?举个例子,通过JNI定义的CallVoidMethod接口调用java方法时,若java方法抛出异常,那么CallVoidMethod方法就会在JNI层抛出这个异常,jvm已经帮我们完成了JNI层和java层抛出异常的“互联互通”工作。

2 JNI层的异常处理 (1)JNI层异常处理方法概述

        我们已经知道,JNI层可以有java异常 和 C/C++异常,那如何处理JNI层的两种异常呢?

JNI层抛的C/C++异常

在JNI层,通过C/C++的try...catch捕获并处理

JNI层抛的java异常

在JNI层,通过JNI定义的与异常相关的接口处理 或 

在java层,通过try...catch捕获并处理

        JNI层的C/C++异常,通过在JNI层 使用C/C++的try...catch捕获并处理,JNI并没有定义处理C/C++异常的接口,JNI不能处理C/C++异常;JNI层的java异常,通过JNI定义的与异常相关的接口捕获并处理,若JNI层不予处理,在JNI方法结束时,会立即将该异常传递给java层,在java层通过对native 添加try...catch,捕获并处理JNI层抛出的java异常。
        那么JNI层抛出java异常后,若不处理,抛出异常的JNI函数会立即结束吗?(即 抛出的异常会立即传给java层吗?)
        不会! JNI函数将继续往下执行,直到遇到return结束 或 没遇到return,执行一段时间后自动结束(具体会执行到函数的什么位置无法确定,一般在函数正常返回之前)

        下面我们说下,在JNI层处理java异常的接口 和 使用,以及如何将JNI接口抛出的异常传递给java层。

(2)涉及到的JNI接口

        下面共7个接口,前6个接口用于检测、抛出、清理异常,最后1个接口用于抛出致命错误。

作用接口
检查当前线程是否有待处理的异常ExceptionOccurred
检查当前线程是否有待处理的异常ExceptionCheck
将异常及堆栈的回溯信息输出到系统错误报告信道(一般是System.err)ExceptionDescribe
清除当前线程待处理的异常ExceptionClear
当前线程抛出 java.lang.ThrowableThrow
当前线程抛出 java.lang.Throwable 或 其子类的 异常对象ThrowNew
抛出致命错误,并结束进程FatalError
(3)接口详解
/**
 * 作用:检查当前线程是否有待处理的异常
 * @return 如果有 返回该异常的引用;如果没有 返回null
 *
 * @exceptions 不报异常
 *
 * 注意:只能检测到调用java代码时抛出的异常 或 调用JNI接口抛出的异常,即只能检测到jvm中抛出的异常
 */
jthrowable ExceptionOccurred();

/**
 * 作用:检查当前线程是否有待处理的异常
 * @return 如果有 返回JNI_TRUE;如果没有 返回JNI_FALSE
 *
 * @exceptions 不报异常
 *
 * 注意:只能检测到调用java代码时抛出的异常 或 调用JNI接口抛出的异常,即只能检测到jvm中抛出的异常
 */
jboolean ExceptionCheck();

/**
 * 作用:将异常及堆栈的回溯信息输出到系统错误报告信道(一般是System.err)
 *
 * @exceptions 不报异常
 */
void ExceptionDescribe();

/**
 * 作用:清除当前线程待处理的异常
 *
 * @exceptions 不报异常
 */
void ExceptionClear();

/**
 * 作用:在当前线程抛出 java.lang.Throwable 异常对象JNI可以通过ExceptionOccurred获取,java层可通过try catch捕获。
 * @param obj
 * @return
 * 0:异常抛出成功
 * 负数:异常抛出失败
 *
 * @exceptions 要抛出的异常
 *
 * 注意:抛出异常后,代码将继续执行
 */
int Throw(jthrowable obj);

/**
 * 作用:在当前线程抛出 java.lang.Throwable 或 其子类的 异常对象,JNI可以通过ExceptionOccurred获取,
 * java层可通过try catch捕获。
 * @param clazz 异常类的class实例
 * @param message 异常信息
 * @return
 * 0:异常抛出成功
 * 负数:异常抛出失败
 *
 * @exceptions 要抛出的异常
 *
 * 注意:抛出异常后,代码将继续执行
 */
jint ThrowNew(jclass clazz, const char* message);

/**
 * 作用:抛出致命错误,并结束进程
 * @param msg 错误信息
 *
 * 注意:调用后立即报错,然后退出应用程序,不论在JNI层还是java层都没有办法阻止,不会执行函数后面代码
 */
void FatalError(const char* msg);
(4)接口使用

        这里介绍下,JNI层如何捕获异常和抛出异常。

/**
 * 抛出异常例子,函数封装如下:
 * 作用:抛出java异常
 * @param env JNIEnv 实例引用
 * @param execptionClassName java异常类 全类名
 * @param msg 异常信息
 */
void throwExecption(JNIEnv * env, const char * execptionClassName, const char * msg){
    jclass jcls = env->FindClass(nullptr == execptionClassName?"java/lang/Throwable":execptionClassName);
    if (jcls != nullptr) {
        //抛出指定名称的异常
        env->ThrowNew(jcls, nullptr == msg? "发生异常":msg);
    }
    env->DeleteLocalRef(jcls);
}

//JNI层异常处理例子
void testExecption(JNIEnv * env){
    //1 模拟JNI接口 或 JNI调用的java方法 抛出的异常
    throwExecption(env,"java/lang/IllegalStateException","我是JNI,我这儿有异常");
    //2 在每个可能抛出异常的JNI接口后面 检测 是否有异常发生
    if(env->ExceptionCheck()){
        //3 获取并缓存JNI层的java异常,用于后续再次抛出
        jthrowable jthrowable = env->ExceptionOccurred();
        //4 清除JNI层抛出的java异常
        env->ExceptionClear();
        //5 TODO:JNI层发生异常的处理逻辑
        //6 重新抛出异常,目的是让java层也收到该异常,并通过try catch捕获并执行java层的相应逻辑
        env->Throw(jthrowable);
        //7 删除局部引用
        env->DeleteLocalRef(jthrowable);
        //8 结束方法,JNI抛出的java异常 传递 到java层
        return;
    }
    // 抛出异常后,如果不处理,会执行该循环
    for(int i = 0; i < 1000; i++){
        __android_log_write(ANDROID_LOG_ERROR, "yy", std::to_string(i).c_str());
    }
}
3 总结及注意

(1)在JNI层,可以有C/C++异常 和 java异常,JNI没有定义自己的异常。
(2)JNI定义的和异常相关的接口只能抛出或处理java异常。
(3)JNI接口抛出java异常后,会继续执行一段时间,然后在正常返回前自动退出,除非遇到return;java层遇到异常则不会执行后续代码,直接执行catch代码块。
(4)JNI和java可以相互捕获并处理对方抛出的java异常。
(5)FatalError接口无法被捕获,一经调用就会退出进程。
(6)JNI层处理java异常时,记得将异常再次抛出到java层,让java层也执行异常捕获逻辑
 

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/3002175.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-09-27
下一篇 2022-09-27

发表评论

登录后才能评论

评论列表(0条)

保存