Android NDK開發之旅14 JNI 異常處理

###異常處理java

異常測試例子:程序員

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代碼以下:bash

//異常處理
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++代碼繼續運行。 例如:ui

//異常處理
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);
}
複製代碼

測試代碼以下:this

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代碼捕獲:spa

//異常處理
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!");
    }
}
複製代碼

測試代碼以下:code

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

}
複製代碼

####異常處理總結get

JNI本身拋出的異常,是Error類型,Java能夠經過Throwable或者Error來捕得到到,捕獲異常後Java代碼能夠繼續執行下去。在C層能夠清空(ExceptionClear),保證try中的代碼Java代碼繼續執行,而且最好要提供補救措施,確保JNI層代碼正常繼續運行。 用戶經過ThrowNew手動拋出的異常,一樣能夠在Java層捕捉獲得。

相關文章
相關標籤/搜索