異常處理
異常測試?yán)樱?/p>
public native void testException1();
public static void main(String[] args) {
JniTest test = new JniTest();
try {
test.testException();
System.out.println("程序無法繼續(xù)執(zhí)行1,這句話不會被打印\n");
} catch (Throwable t) {
System.out.println("捕獲到JNI拋出的異常(Throwable)又厉,這句話會被打印" + t.getMessage() + "\n");
}
System.out.println("程序繼續(xù)執(zhí)行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");
//這里竟然還可以繼續(xù)執(zhí)行
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代碼可以繼續(xù)執(zhí)行下去婆排。
為了確保Java声旺、C/C++代碼可以正常執(zhí)行下去笔链,需要:
在JNI層手動清空異常信息(ExceptionClear)段只,保證代碼可以運(yùn)行。
補(bǔ)救措施保證C/C++代碼繼續(xù)運(yùn)行鉴扫。
例如:
//異常處理
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代碼能夠繼續(xù)執(zhí)行
(*env)->ExceptionClear(env);
//提供補(bǔ)救措施坪创,例如獲取另外一個屬性
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("程序繼續(xù)執(zhí)行莱预,這句話會被打印\n");
}
用戶可以手動通過ThrowNew函數(shù)拋出異常柠掂,同樣可以被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代碼能夠繼續(xù)執(zhí)行
(*env)->ExceptionClear(env);
//提供補(bǔ)救措施涯贞,例如獲取另外一個屬性
fid = (*env)->GetFieldID(env, clz, "key", "Ljava/lang/String;");
}
jstring key = (*env)->GetObjectField(env, jobj, fid);
char* c_str = (*env)->GetStringUTFChars(env, key, NULL);
//參數(shù)不正確,程序員自己拋出異常危喉,可以在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不會繼續(xù)執(zhí)行,這句話不會被打印\n");
} catch (Exception e) {
System.out.println("捕獲到JNI手動拋出的異常辜限,這句話會被打踊始稹:" + e.getMessage() + "\n");
}
System.out.println("程序繼續(xù)執(zhí)行,這句話會被打印\n");
}
異常處理總結(jié)
JNI自己拋出的異常薄嫡,是Error類型氧急,Java可以通過Throwable或者Error來捕獲得到颗胡,捕獲異常后Java代碼可以繼續(xù)執(zhí)行下去态蒂。在C層可以清空(ExceptionClear)杭措,保證try中的代碼Java代碼繼續(xù)執(zhí)行,并且最好要提供補(bǔ)救措施钾恢,確保JNI層代碼正常繼續(xù)運(yùn)行。
用戶通過ThrowNew手動拋出的異常崩哩,同樣可以在Java層捕捉得到汹押。