###異常處理
異常測試例子:
public native void testException1();
public static void main(String[] args) {
JniTest test = new JniTest();
try {
test.testException();
System.out.println("程式無法繼續執行1,這句話不會被列印\n");
} catch (Throwable t) {
System.out.println("捕獲到JNI丟擲的異常(Throwable),這句話會被列印" + t.getMessage() + "\n");
}
System.out.println("程式繼續執行2,這句話會被列印\n");
}
複製程式碼
C程式碼如下:
//異常處理
JNIEXPORT void JNICALL Java_com_test_JniTest_testException1
(JNIEnv * env, jobject jobj){
jclass clz= (*env)->GetObjectClass(env, jobj);
//屬性名字不小心寫錯了,拿到的是空的jfieldID
jfieldID fid = (*env)->GetFieldID(env, clz, "key1", "Ljava/lang/String;");
//此處丟擲的異常,Java可以通過Throwable來捕獲
printf("C can run , this will print");
//這裡竟然還可以繼續執行
jstring key = (*env)->GetObjectField(env, jobj, fid);
//遇到這句話的時候,C程式Crash了
char* c_str = (*env)->GetStringUTFChars(env, key, NULL);
printf("C could not run , this will not print");
}
複製程式碼
通過例子可以知道,JNI層自己丟擲的異常是Error型別,Java可以通過Throwable或者Error來捕獲得到,捕獲異常後Java程式碼可以繼續執行下去。
#####為了確保Java、C/C++程式碼可以正常執行下去,需要:
在JNI層手動清空異常資訊(ExceptionClear),保證程式碼可以執行。 補救措施保證C/C++程式碼繼續執行。 例如:
//異常處理
JNIEXPORT void JNICALL Java_com_test_JniTest_testException1
(JNIEnv * env, jobject jobj){
jclass clz = (*env)->GetObjectClass(env, jobj);
//屬性名字不小心寫錯了,拿到的是空的jfieldID
jfieldID fid = (*env)->GetFieldID(env, clz, "key1", "Ljava/lang/String;");
jthrowable err = (*env)->ExceptionOccurred(env);
if (err != NULL){
//手動清空異常資訊,保證Java程式碼能夠繼續執行
(*env)->ExceptionClear(env);
//提供補救措施,例如獲取另外一個屬性
fid = (*env)->GetFieldID(env, clz, "key", "Ljava/lang/String;");
}
jstring key = (*env)->GetObjectField(env, jobj, fid);
char* c_str = (*env)->GetStringUTFChars(env, key, NULL);
}
複製程式碼
測試程式碼如下:
public static void main(String[] args) {
JniTest test = new JniTest();
try {
test.testException();
System.out.println("程式沒有異常,這句話會被列印\n");
} catch (Exception e) {
System.out.println("沒有捕獲到JNI丟擲的異常,這句話不會被列印" + e.getMessage() + "\n");
}
System.out.println("程式繼續執行,這句話會被列印\n");
}
複製程式碼
使用者可以手動通過ThrowNew函式丟擲異常,同樣可以被Java程式碼捕獲:
//異常處理
JNIEXPORT void JNICALL Java_com_test_JniTest_testException
(JNIEnv * env, jobject jobj){
jclass clz = (*env)->GetObjectClass(env, jobj);
//屬性名字不小心寫錯了,拿到的是空的jfieldID
jfieldID fid = (*env)->GetFieldID(env, clz, "key1", "Ljava/lang/String;");
jthrowable err = (*env)->ExceptionOccurred(env);
if (err != NULL){
//手動清空異常資訊,保證Java程式碼能夠繼續執行
(*env)->ExceptionClear(env);
//提供補救措施,例如獲取另外一個屬性
fid = (*env)->GetFieldID(env, clz, "key", "Ljava/lang/String;");
}
jstring key = (*env)->GetObjectField(env, jobj, fid);
char* c_str = (*env)->GetStringUTFChars(env, key, NULL);
//引數不正確,程式設計師自己丟擲異常,可以在Java中捕獲
if (_stricmp(c_str,"efg") != 0){
jclass err_clz = (*env)->FindClass(env, "java/lang/IllegalArgumentException");
(*env)->ThrowNew(env, err_clz, "key value is invalid!");
}
}
複製程式碼
測試程式碼如下:
public static void main(String[] args) {
JniTest test = new JniTest();
try {
test.testException();
System.out.println("JNI手動丟擲了異常,Java不會繼續執行,這句話不會被列印\n");
} catch (Exception e) {
System.out.println("捕獲到JNI手動丟擲的異常,這句話會被列印:" + e.getMessage() + "\n");
}
System.out.println("程式繼續執行,這句話會被列印\n");
}
複製程式碼
####異常處理總結
JNI自己丟擲的異常,是Error型別,Java可以通過Throwable或者Error來捕獲得到,捕獲異常後Java程式碼可以繼續執行下去。在C層可以清空(ExceptionClear),保證try中的程式碼Java程式碼繼續執行,並且最好要提供補救措施,確保JNI層程式碼正常繼續執行。 使用者通過ThrowNew手動丟擲的異常,同樣可以在Java層捕捉得到。