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
#include
#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
#include
#include
#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, "",
"()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);