Android JNI开发系列(十三) JNI异常处理

北战南征 提交于 2019-11-27 13:45:37

JNI 异常处理

JNI异常与JAVA处理异常的区别

  • JAVA 有异常处理机制,而JNI没有
  • 如果JAVA中异常没有捕获,后面的代码不会执行,JNI会执行
  • JAVA编译时的异常,是在方法显示的声明了某一个异常,编译器要求在调用的时候必须显示的捕获

JNI异常举例分析

package org.professor.jni.java;

public class TestException {

    public native void testException();

    public void exceptionCallback() {
        int a = 20 / 0;
        System.out.println("--->" + a);
    }
}

package org.professor.jni;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;

import org.professor.jni.java.TestException;

public class MainActivity extends AppCompatActivity {

    // Used to load the 'native-lib' library on application startup.
    static {
        // lib+native-lib+.so
        //libnative-lib.so
        System.loadLibrary("hello-jni");
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        TextView tv = (TextView) findViewById(R.id.sample_text);
//        tv.setText(stringFromJNI());
        TestException testException = new TestException();
        try {
            testException.testException();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

#include <jni.h>
#include <android/log.h>

#ifndef ANDROIDJNIDEMO_TEST_THROW_EXCEPTION_H
#define ANDROIDJNIDEMO_TEST_THROW_EXCEPTION_H


#define LOG_D(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define LOG_I(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOG_W(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)
#define LOG_E(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)

#ifdef __cplusplus
extern "C" {
#endif
/*
 *
 * Class:     org_professor_jni_java_TestException
 * Method:    occorException
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_org_professor_jni_java_TestException_testException
        (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif //ANDROIDJNIDEMO_TEST_THROW_EXCEPTION_H

//------分割线

#include <jni.h>
#include <test_throw_exception.h>
#include <stdio.h>

#define LOG_TAG "EXCEPTION"


JNIEXPORT void JNICALL
Java_org_professor_jni_java_TestException_testException(JNIEnv *env, jobject instance) {


    jclass exception_clazz = (*env)->FindClass(env, "org/professor/jni/java/TestException");
    if (NULL == exception_clazz) {
        LOG_D("NOT FIND EXCEPTION CLASS");
        return;
    }

    jmethodID test_method_id = (*env)->GetMethodID(env, exception_clazz, "exceptionCallback",
                                                   "()V");
    if (NULL == test_method_id) {
        LOG_E("NOT FIND TEXT METHOD ID");
        return;
    }

    jmethodID default_constructor_method_id = (*env)->GetMethodID(env, exception_clazz, "<init>",
                                                                  "()V");
    if (NULL == default_constructor_method_id) {
        LOG_I("NOT FIND DEFAULT CONSTRUCTOR ID ");
        return;
    }

    jobject test_exception_instance = (*env)->NewObject(env, exception_clazz,
                                                        default_constructor_method_id);
    if (NULL == test_exception_instance) {
        LOG_W("NOT FIND TEST EXCEPTION INSTANCE");
        return;
    }

    (*env)->CallVoidMethod(env, test_exception_instance, test_method_id);

    LOG_E("In C: CALL exceptionCallback Method!");
//    if ((*env)->ExceptionCheck(env)) {  // 检查JNI调用是否有引发异常
//        (*env)->ExceptionDescribe(env);
////        (*env)->ExceptionClear(env);        // 清除引发的异常,在Java层不会打印异常的堆栈信息
//          如果不清除,后面调用ThrowNew抛出的异常堆栈信息会覆盖前面的异常信息
////        (*env)->ThrowNew(env, (*env)->FindClass(env, "java/lang/Exception"), "JNI抛出的异常!"); //清除后可以抛出自己的异常 如果不清除,后面调用ThrowNew抛出的异常堆栈信息会覆盖前面的异常信息
//        //return;
//    }
    jthrowable throwable = (*env)->ExceptionOccurred(env);
    if (NULL != throwable) {
        (*env)->ExceptionDescribe(env);
//        (*env)->ExceptionClear(env);
    }


    LOG_E("In C: CALL exceptionCallback Method! end");

    (*env)->DeleteLocalRef(env, exception_clazz);
    (*env)->DeleteLocalRef(env, test_exception_instance);

}


分析:

  • 首先在Java代码中,定义一个TestException 类,里面定义了一个native方法,和一个普通方法,在普通方法里面正常会抛出一个异常ArithmeticException
  • 在native代码中调用了exceptionCallback方法,并对其进行异常检查(可以用两种方法对其进行内存检查,代码注释)。

总结

  • 当调用一个JNI函数后,必须先检查、处理、清除异常后再做其它 JNI 函数调用,否则会产生不可预知的结果。

  • 一旦发生异常,立即返回,让调用者处理这个异常。或 调用 ExceptionClear 清除异常,然后执行自己的异常处理代码。

  • 异常处理的相关JNI函数总结:

    • ExceptionCheck:检查是否发生了异常,若有异常返回JNI_TRUE,否则返回JNI_FALSE
    • ExceptionOccurred:检查是否发生了异常,若用异常返回该异常的引用,否则返回NULL
    • ExceptionDescribe:打印异常的堆栈信息
    • ExceptionClear:清除异常堆栈信息
    • ThrowNew:在当前线程触发一个异常,并自定义输出异常信息 jint (JNICALL *ThrowNew) (JNIEnv *env, jclass clazz, const char *msg);
    • Throw:丢弃一个现有的异常对象,在当前线程触发一个新的异常 jint (JNICALL *Throw) (JNIEnv *env, jthrowable obj);
    • FatalError:致命异常,用于输出一个异常信息,并终止当前VM实例(即退出程序) void (JNICALL *FatalError) (JNIEnv *env, const char *msg);
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!