2008-10-23 82 views
46

我想要一個一致的和簡單的方法來在JNI代碼中拋出異常;處理鏈式異常的東西(隱式地來自env-> ExceptionOccurred方法,或者通過參數明確指示,無論哪種方式都是好的),並且每次我想要這樣做時都可以節省我查找構造函數的工作。以上所有內容最好都用C語言編寫,儘管我可以根據需要從C++翻譯它。在JNI代碼中拋出異常的最佳方法?

有沒有人有這樣的東西,他們可以分享?

+0

'處理鏈式異常'是否意味着您的代碼會注意到從Java到C++的返回時會捕獲Java級別的異常,並將其包裝在其他一些異常中,並將新異常從C++重新提交給Java? – 2012-08-31 11:49:01

回答

38

我們只是爲每種我們想拋出的異常類型編寫實用方法。下面是一些例子:

jint throwNoClassDefError(JNIEnv *env, char *message) 
{ 
    jclass exClass; 
    char *className = "java/lang/NoClassDefFoundError"; 

    exClass = (*env)->FindClass(env, className); 
    if (exClass == NULL) { 
     return throwNoClassDefError(env, className); 
    } 

    return (*env)->ThrowNew(env, exClass, message); 
} 

jint throwNoSuchMethodError(
     JNIEnv *env, char *className, char *methodName, char *signature) 
{ 

    jclass exClass; 
    char *exClassName = "java/lang/NoSuchMethodError" ; 
    LPTSTR msgBuf; 
    jint retCode; 
    size_t nMallocSize; 

    exClass = (*env)->FindClass(env, exClassName); 
    if (exClass == NULL) { 
     return throwNoClassDefError(env, exClassName); 
    } 

    nMallocSize = strlen(className) 
      + strlen(methodName) 
      + strlen(signature) + 8; 

    msgBuf = malloc(nMallocSize); 
    if (msgBuf == NULL) { 
     return throwOutOfMemoryError 
       (env, "throwNoSuchMethodError: allocating msgBuf"); 
    } 
    memset(msgBuf, 0, nMallocSize); 

    strcpy(msgBuf, className); 
    strcat(msgBuf, "."); 
    strcat(msgBuf, methodName); 
    strcat(msgBuf, "."); 
    strcat(msgBuf, signature); 

    retCode = (*env)->ThrowNew(env, exClass, msgBuf); 
    free (msgBuf); 
    return retCode; 
} 

jint throwNoSuchFieldError(JNIEnv *env, char *message) 
{ 
    jclass exClass; 
    char *className = "java/lang/NoSuchFieldError" ; 

    exClass = (*env)->FindClass(env, className); 
    if (exClass == NULL) { 
     return throwNoClassDefError(env, className); 
    } 

    return (*env)->ThrowNew(env, exClass, message); 
} 

jint throwOutOfMemoryError(JNIEnv *env, char *message) 
{ 
    jclass exClass; 
    char *className = "java/lang/OutOfMemoryError" ; 

    exClass = (*env)->FindClass(env, className); 
    if (exClass == NULL) { 
     return throwNoClassDefError(env, className); 
    } 

    return (*env)->ThrowNew(env, exClass, message); 
} 

這樣一來,很容易找到他們,你的代碼完成編輯器將幫助您鍵入他們,你可以通過簡單的參數。

我相信你可以擴展它以處理鏈式異常或其他更復雜的方法。這足以滿足我們的需求。

+20

剛剛發現這個,謝謝。但是,`throwNoClassDefError`中的錯誤條件不會導致無限遞歸和不可避免的堆棧溢出?我承認,這絕不應該發生,但這似乎不是處理它的適當方式。也許回到`java.lang.error`和`abort()`之類的東西,如果不行的話。 – 2010-09-19 00:42:54

+0

是的,我也看到了。同意。我無法讓我的ThrowNew()調用做_anything_,即使它們返回NULL(即成功)。 Nothin的永遠容易... – 2012-02-14 15:25:44

15

我只需使用2線:

sprintf(exBuffer, "NE%4.4X: Caller can %s %s print", marker, "log", "or"); 
(*env)->ThrowNew(env, (*env)->FindClass(env, "java/lang/Exception"), exBuffer); 

產地:

Exception in thread "main" java.lang.Exception: NE0042: Caller can log or print. 
6

我的代碼在Java中開始,調用C++,然後再次調用Java的東西比如尋找,獲得和設置字段值。

萬一有人找一個C++的辦法找到這個頁面,我將與此犁地:

什麼我現在做的是包裝我的JNI方法體了一個C++ try/catch塊,

JNIEXPORT void JNICALL Java_com_pany_jni_JNIClass_something(JNIEnv* env, jobject self) 
{ 
    try 
    { 
     ... do JNI stuff 
     // return something; if not void. 
    } 
    catch (PendingException e) // (Should be &e perhaps?) 
    { 
     /* any necessary clean-up */ 
    } 
} 

其中PendingException是平凡聲明:

class PendingException {}; 

,我調用調用從任何C JNI後,下面的方法++,因此,如果Java異常狀態指示S上的錯誤,我會立刻保釋,並讓普通的Java異常處理添加(本機方法)線堆棧跟蹤,同時給了C++的機會,清理,同時展開:

PendingException PENDING_JNI_EXCEPTION; 
void throwIfPendingException(JNIEnv* env) 
{ 
    if (env->ExceptionCheck()) { 
     throw PENDING_JNI_EXCEPTION; 
    } 
} 

我的Java堆棧跟蹤看起來像這樣的失敗env-> GetFieldId()調用:

java.lang.NoSuchFieldError: no field with name='opaque' signature='J' in class Lcom/pany/jni/JniClass; 
    at com.pany.jni.JniClass.construct(Native Method) 
    at com.pany.jni.JniClass.doThing(JniClass.java:169) 
    at com.pany.jni.JniClass.access$1(JniClass.java:151) 
    at com.pany.jni.JniClass$2.onClick(JniClass.java:129) 
    at android.view.View.performClick(View.java:4084) 

和相當類似,如果我打電話到那個拋出一個Java方法:

java.lang.RuntimeException: YouSuck 
    at com.pany.jni.JniClass.fail(JniClass.java:35) 
    at com.pany.jni.JniClass.getVersion(Native Method) 
    at com.pany.jni.JniClass.doThing(JniClass.java:172) 

我不能跟包裝在C++中另一個Java異常中的Java異常,我認爲這是你問題的一部分 - 我還沒有發現需要這樣做 - 但是如果我這樣做了,我要麼用一個Java級別的包裝器本地方法,或者只是擴展我的異常拋出方法來獲取jThrowable,並用醜陋的東西替換env-> ThrowNew()調用:不幸的是,Sun並沒有提供ThrowNew的一個版本,它帶有一個可jThrowable。

void impendNewJniException(JNIEnv* env, const char *classNameNotSignature, const char *message) 
{ 
    jclass jClass = env->FindClass(classNameNotSignature); 
    throwIfPendingException(env); 
    env->ThrowNew(jClass, message); 
} 

void throwNewJniException(JNIEnv* env, const char* classNameNotSignature, const char* message) 
{ 
    impendNewJniException(env, classNameNotSignature, message); 
    throwIfPendingException(env); 
} 

我不會考慮緩存(異常)類的構造函數的引用,因爲異常不應該是一個通用控制流機制,所以如果他們慢它不應該的問題。我想,無論如何,查找速度並不是很慢,因爲Java可能會爲這類事情做自己的緩存。

相關問題