在Java的編程中,我們經常會遇到各種的異常,也會處理各種的異常。處理異常在java中非常簡單,我們通常會使用try-catch-finally來處理,也可以使用throw簡單拋出一個異常。那么在jni編程的時候我們又是如何處理異常的呢?
異常處理流程
jni規范已經給我們做好了所有需要做的事情。回想一下處理異常的過程:
- 我們首先要在有可能產生異常的地方檢測異常
- 處理異常
是的,我覺得異常的處理就是可以簡單的總結為兩步,在異常處理中我們通常會打印棧信息等。在jni編程中,異常處理的思路應該也與之類似,過程可用下圖表示:
在jni中我么需要手動清除異常。因此,jni中異常的處理應該嚴格遵循:檢查->處理->清除的流程,在圖中我把清除也認為是處理的其中一步了。
JNI中異常處理函數
jni.h中有如下相關的函數的定義:
jint (*Throw)(JNIEnv*, jthrowable); jint (*ThrowNew)(JNIEnv *, jclass, const char *); jthrowable (*ExceptionOccurred)(JNIEnv*); void (*ExceptionDescribe)(JNIEnv*); void (*ExceptionClear)(JNIEnv*); void (*FatalError)(JNIEnv*, const char*);
此外,還有一個函數和他們沒放在一起:
jboolean (*ExceptionCheck)(JNIEnv*);
單從名字上我們可以知道用於檢測異常的發生的函數有:
- (ExceptionCheck)(JNIEnv);
- (ExceptionOccurred)(JNIEnv);
用於清理異常的有: - (ExceptionClear)(JNIEnv);
用於跑出異常的有: - (Throw)(JNIEnv, jthrowable);
- (ThrowNew)(JNIEnv , jclass, const char *);
- (FatalError)(JNIEnv, const char*);
下面對以上函數做一個簡單的介紹:
1> ExceptionCheck:檢查是否發生了異常,若有異常返回JNI_TRUE,否則返回JNI_FALSE
2> ExceptionOccurred:檢查是否發生了異常,若用異常返回該異常的引用,否則返回NULL
3> ExceptionDescribe:打印異常的堆棧信息
4> ExceptionClear:清除異常堆棧信息
5> ThrowNew:在當前線程觸發一個異常,並自定義輸出異常信息
6> Throw:丟棄一個現有的異常對象,在當前線程觸發一個新的異常
7> FatalError:致命異常,用於輸出一個異常信息,並終止當前VM實例(即退出程序)
測試異常
根據以上總結,我們寫如下功能的測試代碼:
native調用java中的方法,java中的方法拋出異常,我們在native中檢測異常,檢測到后拋出native中的異常,並清理異常。
c代碼
void native_catchException(JNIEnv *env, jobject obj) { jthrowable exc; jclass cls = (*env)->GetObjectClass(env, obj); jmethodID mid =(*env)->GetMethodID(env, cls, "callbackException", "()V"); if (mid == NULL) { return; } (*env)->CallVoidMethod(env, obj, mid); exc = (*env)->ExceptionOccurred(env); if (exc) { jclass newExcCls; (*env)->ExceptionDescribe(env); (*env)->ExceptionClear(env); newExcCls = (*env)->FindClass(env,"java/lang/IllegalArgumentException"); if (newExcCls == NULL) { /* Unable to find the exception class, give up. */ return; } (*env)->ThrowNew(env, newExcCls, "thrown from C code"); } } static JNINativeMethod gMethods[] = { ... {"exception","()V",(void *)native_catchException}, };
代碼的其他部分請參看之前的章節。
java代碼
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); textView = (TextView) findViewById(R.id.text); try { exception(); } catch (Exception e) { System.out.println("In Java:\n\t" + e); } } private native void exception() throws IllegalArgumentException; private void callbackException() throws NullPointerException { throw new NullPointerException("MainActivity.callbackException"); }
輸出
09-26 10:58:50.012 23934-23934/com.jinwei.jnitesthello W/System.err: java.lang.NullPointerException: MainActivity.callbackException 09-26 10:58:50.012 23934-23934/com.jinwei.jnitesthello W/System.err: at com.jinwei.jnitesthello.MainActivity.callbackException(MainActivity.java:24) 09-26 10:58:50.012 23934-23934/com.jinwei.jnitesthello W/System.err: at com.jinwei.jnitesthello.MainActivity.exception(Native Method) 09-26 10:58:50.012 23934-23934/com.jinwei.jnitesthello W/System.err: at com.jinwei.jnitesthello.MainActivity.onCreate(MainActivity.java:32) 09-26 10:58:50.013 23934-23934/com.jinwei.jnitesthello W/System.err: at android.app.Activity.performCreate(Activity.java:6299) 09-26 10:58:50.013 23934-23934/com.jinwei.jnitesthello W/System.err: at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1107) 09-26 10:58:50.013 23934-23934/com.jinwei.jnitesthello W/System.err: at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2369) 09-26 10:58:50.013 23934-23934/com.jinwei.jnitesthello W/System.err: at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2476) 09-26 10:58:50.013 23934-23934/com.jinwei.jnitesthello W/System.err: at android.app.ActivityThread.-wrap11(ActivityThread.java) 09-26 10:58:50.013 23934-23934/com.jinwei.jnitesthello W/System.err: at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1344) 09-26 10:58:50.013 23934-23934/com.jinwei.jnitesthello W/System.err: at android.os.Handler.dispatchMessage(Handler.java:102) 09-26 10:58:50.013 23934-23934/com.jinwei.jnitesthello W/System.err: at android.os.Looper.loop(Looper.java:148) 09-26 10:58:50.013 23934-23934/com.jinwei.jnitesthello W/System.err: at android.app.ActivityThread.main(ActivityThread.java:5417) 09-26 10:58:50.013 23934-23934/com.jinwei.jnitesthello W/System.err: at java.lang.reflect.Method.invoke(Native Method) 09-26 10:58:50.013 23934-23934/com.jinwei.jnitesthello W/System.err: at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:731) 09-26 10:58:50.013