2008-10-23 11 views
46

Chciałbym spójny i prosty sposób zgłaszania wyjątków w kodzie JNI; coś, co obsługuje przykręcone wyjątki (domyślnie z metody env-> ExceptionOccurred lub jawnie przez parametry, tak czy inaczej jest dobra) i oszczędza mi wyszukiwanie konstruktorów za każdym razem gdy chcę to zrobić. Wszystkie powyższe są najlepiej w C, chociaż w razie potrzeby mogę je przetłumaczyć z C++.Najlepszy sposób na zgłaszanie wyjątków w kodzie JNI?

Czy każdy użytkownik ma coś takiego, co może udostępnić?

+0

Przez „uchwyty przykuty wyjątki:” Czy to znaczy, że Twój kod będzie zauważyć złapać wyjątek Java poziomu po powrocie z Java do C++, zawinąć go w jakimś innym wyjątku, i rzucać, że nowy wyjątek z powrotem z C++ do Java? –

Odpowiedz

38

Po prostu kodujemy metody użytkowe dla każdego z typów wyjątków, które chcemy rzucić. Oto kilka przykładów:

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

W ten sposób łatwo jest je znaleźć, edytor kodu ukończenie pomoże Ci je wpisać, a można przejść prostych parametrów.

Jestem pewna, że ​​można to rozszerzyć, aby obsługiwać powiązane wyjątki lub inne bardziej skomplikowane podejścia. To wystarczyło, aby sprostać naszym potrzebom.

+20

Właśnie znalazłem to, dzięki. Jednak czy warunek błędu w 'throwNoClassDefError' nie spowoduje nieskończonej rekursji i nieuniknionego przepełnienia stosu? To naprawdę nie powinno się zdarzyć, przyznaję, ale to nie wydaje się być właściwym sposobem radzenia sobie z tym. Być może powróć do 'java.lang.error' i' abort() 'lub czegoś, jeśli to nie działa. –

+0

Tak, też to widziałem. Zgoda. Nie mogę uzyskać swoich wywołań funkcji ThrowNew(), aby zrobić _nie_ nic, nawet jeśli zwracają wartość NULL (sukces, to znaczy). Nic kiedykolwiek proste ... –

+1

Niestety, ale może ktoś mi wyjaśnić dlaczego funkcja throwNoClassDefError nie spadnie w nieskończonej rekurencji w przypadku, gdy klasa „java/lang/NoClassDefFoundError” nie będzie można znaleźć? –

15

po prostu użyć 2 linie:

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

produkuje:

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

mi dać do zrozumienia, że ​​łowienie java.lang.Exception uważany jest za słabe praktyki: Rzucam com.mycompany.JniException gdzie chcę ogólnym przypadku awarii JNI. –

+10

@ android.weasel: Dude, to przykładowy kod na StackOverflow, aby zilustrować ThrowNew API. Nie ma to być kod produkcyjny na serwerze o znaczeniu krytycznym. Odpuść mu... – deltamind106

6

Mój kod rozpoczyna się w Javie, wywołuje C++, które następnie wywołuje Java powrotem do rzeczy jak znalezienie, uzyskiwanie i ustawienia wartości pól.

W przypadku gdy ktoś szuka C++ podejście znajdzie tej strony, będę orać na z tym:

Co mam teraz robić jest owijanie moje ciał metody JNI się z C++ try/bloku 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 */ 
    } 
} 

gdzie PendingException deklaruje trywialnie:

class PendingException {}; 

i jestem powołując się następującą metodę po wywołaniu jakiegokolwiek JNI z C++, więc jeśli stan wyjątek Java wskazują s błąd, będę natychmiast ratować i niech normalne obsługa wyjątków Java dodać (metoda podstawowa) linię do śledzenia stosu, dając C++ możliwość oczyścić podczas odwijania:

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

Moje stosu Java ślad wygląda to dla nieudanej env-> GetFieldId() połączenia:

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) 

i dość podobny, jeśli zadzwonię do metody Java, który rzuca:

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) 

nie mogę mówić do owijania wyjątek Javy w innym wyjątku Java z C++, co moim zdaniem jest częścią twojego pytania - nie znalazłem potrzeby, aby to zrobić - ale gdybym to zrobił, zrobiłbym to za pomocą wrappera na poziomie Java dookoła natywne metody, lub po prostu poszerz moje metody rzucania wyjątków, aby wziąć jst i zastąpić połączenie env-> ThrowNew() czymś brzydkim: to niefortunne, że Sun nie dostarczył wersji ThrowNew, która zabrała jst.

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

nie uważam referencje buforowanie (wyjątek) Konstruktor klasy ponieważ wyjątki nie mają być zwykły mechanizm przepływu sterowania, więc to nie powinno mieć znaczenia, czy są wolne. Wyobrażam sobie, że look-up nie jest tak strasznie powolny, ponieważ Java prawdopodobnie robi własne buforowanie tego typu rzeczy.

Powiązane problemy