Android C++系列:JNI調用時的異常處理

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Android JNI 調用時的異常主要有如下兩種:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Native 代碼調用 Java 層代碼時發生了異常要處理","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Native 代碼自己拋出了一個異常讓 Java 層去處理 可以看到異常的發生和處理基本都需要 Native 和 Java 交互,而對於 Native 自身出了異常,也就是 C/C++ 代碼有問題,導致應用崩潰的又是另一回事了。","attrs":{}}]}]}],"attrs":{}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"Native 調用 Java 方法時的異常","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"之前講述瞭如何從 Native 調用 Java 的方法,先準備一個有異常的方法供 Native 去調用。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"private int exceptionFun() { return 2 / 0; } \n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"然後在 Native 中調用該方法:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"jclass cls = env->FindClass(\"com/qingkouwei/Demo\");\n jmethodID mid = env->GetMethodID(cls, \"\", \"()V\");\n jobject obj = env->NewObject(cls, mid);\n mid = env->GetMethodID(cls, \"exceptionFun\", \"()I\");\n // 先初始化一個類,然後調用類方法,就如文章中描述的那樣\n env->CallIntMethod(obj, mid);\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"除數爲 0 ,一調用應用直接崩潰了。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":" java.lang.ArithmeticException: divide by zero \n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"接下來就是要進行異常處理的部分了。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"jclass cls = env->FindClass(\"com/qingkouwei/Demo\");\n jmethodID mid = env->GetMethodID(cls, \"\", \"()V\");\n jobject obj = env->NewObject(cls, mid);\n mid = env->GetMethodID(cls, \"exceptionFun\", \"()I\");\n\n // 先初始化一個類,然後調用類方法,就如博客中描述的那樣\n env->CallIntMethod(obj, mid);\n //檢查是否發生了異常\n jthrowable exc = env->ExceptionOccurred();\n // jboolean result = env->ExceptionCheck();\n\n if (exc) {\n // 打印異常日誌\n env->ExceptionDescribe();\n // 這行代碼纔是關鍵不讓應用崩潰的代碼,\n env->ExceptionClear();\n // 發生異常了要記得釋放資源\n env->DeleteLocalRef(cls);\n env->DeleteLocalRef(obj);\n }\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"添加如上代碼進行處理後,應用並不會直接崩潰了,並且在 LogCat 中會看到對應的異常日誌,這裏面到了做了哪些操作呢?","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Native 提供了 ExceptionOccurred 和 ExceptionCheck 方法來檢測是否有異常發生,前者返回的是 jthrowable 類型,後者返回的是 jboolean 類型。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果有異常,會通過 ExceptionDescribe 方法來打印異常信息,方便我們在 LogCat 中看到對應的信息。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"而 ExceptionClear 方法則是關鍵的不會讓應用直接崩潰的方法,類似於 Java 的 catch 捕獲異常處理,它會消除這次異常。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這樣就把由 Native 調用 Java 時的一個異常進行了處理,當處理完異常之後,別忘了釋放對應的資源。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"不過,我們這樣僅僅是消除了這次異常,還應該讓調用者有異常的發生,那麼就需要通過 Native 來拋出一個異常告訴 Java 調用者了。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"Native 拋出 Java 中的異常","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"有時在 Native 代碼中進行一些操作,需要拋出異常到 Java ,交由上層去處理。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"比如 Java 調用 Native 方法傳遞了某個參數,而這個參數有問題,那麼 Native 就可以拋出異常讓 Java 去處理這個參數異常的問題。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Native 拋出異常的代碼大致都是相同的,可以抽出一個通用函數來:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"void throwByName(JNIEnv *env, const char *name, const char *msg) {\n jclass cls = env->FindClass(name);\n if (cls != NULL) {\n env->ThrowNew(cls, msg);\n }\n env->DeleteLocalRef(cls);\n }\n // 調用拋出異常\n extern \"C\"\n JNIEXPORT void JNICALL\n Java_com_qingkouwei_Demo_nativeThrowException(JNIEnv *env, jobject instance) {\n throwByName(env, \"java/lang/IllegalArgumentException\", \"native throw exception\");\n }\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"​","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"根據異常類型和異常信息來拋出異常。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"而在 Java 中,就必須要用 try … catch… 來準備好捕獲這次異常了。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"try {\n nativeThrowException();\n } catch (IllegalArgumentException e) {\n Log.e(\"NativeMethod\", e.getMessage());\n }\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"​","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"小結","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"除了以上兩種異常情況之外,還有一個特別常見的異常,就是當判斷某個變量爲 NULL 之後,執行立即返回的操作。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當發生異常時,一定要先處理異常,然後才能繼續執行後面的步驟。如果不是需要立即返回的,那麼就通過 ExceptionClear清除這次異常,然後在進行其他的處理。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"對於在 Native 中發生了異常,需要讓 Java 層去處理了,則在 Native 中拋出對應的異常,由 Java 層去捕獲,比如在使用 ExceptionClear 清除了異常之後,就可以通過 throwNew 來拋出異常信息。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"具體的異常處理方法和時機還是要看具體的使用場景,選擇最合適的處理方法。","attrs":{}}]}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章