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);