2008-10-23 37 views
46

Tôi muốn một cách nhất quán và đơn giản để ném ngoại lệ trong mã JNI; một cái gì đó xử lý các trường hợp ngoại lệ bị xích (ngầm định từ phương thức en--> ExceptionOccurred, hoặc một cách rõ ràng bởi các tham số, một trong hai cách là tốt) và giúp tôi tìm kiếm các nhà xây dựng mỗi khi tôi muốn làm điều này. Tất cả những điều trên là tốt nhất trong C, mặc dù tôi có thể dịch nó từ C++ khi cần.Cách tốt nhất để ném ngoại lệ trong mã JNI?

Có ai trên SO có nội dung như thế này mà họ có thể chia sẻ không?

+0

Bằng cách 'xử lý các trường hợp ngoại lệ' bạn có nghĩa là mã của bạn sẽ nhận thấy một ngoại lệ Java khi trở về từ Java đến C++, bọc nó trong một ngoại lệ khác và ném ngoại lệ mới này từ C++ lên Java? –

Trả lời

38

Chúng tôi chỉ viết các phương thức tiện ích cho từng loại ngoại lệ mà chúng tôi muốn ném. Dưới đây là một số ví dụ:

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

Bằng cách đó, thật dễ dàng để tìm thấy chúng, biên tập mã hoàn thành của bạn sẽ giúp bạn gõ chúng vào, và bạn có thể vượt qua các thông số đơn giản.

Tôi chắc chắn bạn có thể mở rộng điều này để xử lý các trường hợp ngoại lệ bị xích hoặc các cách tiếp cận phức tạp hơn khác. Điều này là đủ để đáp ứng nhu cầu của chúng tôi.

+20

Chỉ tìm thấy điều này, cảm ơn. Tuy nhiên, sẽ không phải là điều kiện lỗi trong kết quả 'throwNoClassDefError' trong đệ quy vô hạn và tràn ngăn không thể tránh khỏi? Nó thực sự không bao giờ xảy ra, tôi thừa nhận, nhưng điều đó dường như không phải là cách thích hợp để xử lý nó. Có lẽ rơi vào 'java.lang.error', và' abort() 'hoặc cái gì đó nếu nó không hoạt động. –

+0

Vâng, tôi cũng thấy điều đó. Đã đồng ý. Tôi không thể nhận được ThrowNew() các cuộc gọi của tôi để làm _anything_, mặc dù họ trả về NULL (thành công, đó là). Nothin bao giờ dễ dàng ... –

+1

Xin lỗi, nhưng bất cứ ai có thể giải thích cho tôi lý do tại sao các throwNoClassDefError chức năng sẽ không rơi vào đệ quy vô hạn trong trường hợp khi "java/lang/NoClassDefFoundError" lớp học sẽ không được tìm thấy? –

15

Tôi chỉ cần sử dụng 2 dòng:

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

Tạo:

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

Tôi được cho là hiểu rằng bắt java.lang.Exception được coi là thực hành kém: Tôi đang ném com.mycompany.JniException nơi tôi muốn một trường hợp thất bại chung JNI. –

+10

@ android.weasel: Dude, đó là mã mẫu trên StackOverflow để minh họa cho API ThrowNew. Nó không phải là mã sản xuất trong một máy chủ nhiệm vụ quan trọng. Hãy cho anh chàng nghỉ ngơi... – deltamind106

6

Mã của tôi bắt đầu bằng Java, gọi C++, sau đó gọi Java trở lại một lần nữa cho những thứ như tìm kiếm, nhận được, và thiết lập giá trị trường.

Trong trường hợp ai đó tìm kiếm một C++ cách tiếp cận tìm thấy trang này, tôi sẽ cày trên với điều này:

Những gì tôi đang bây giờ làm được gói cơ quan phương pháp JNI của tôi với một C++ try/catch block,

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 */ 
    } 
} 

nơi PendingException được khai báo trivially:

class PendingException {}; 

và tôi đang gọi phương thức sau đây sau khi gọi bất kỳ JNI từ C++, vì vậy nếu tình trạng Java ngoại lệ chỉ s lỗi, tôi sẽ giải cứu ngay lập tức và để cho các xử lý ngoại lệ Java bình thường thêm (phương pháp Native) dòng vào vết đống, trong khi cho C++ cơ hội để dọn dẹp trong khi tháo:

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

Java chồng của tôi dấu vết trông như thế này cho một env-> GetFieldId() gọi thất bại:

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) 

và khá tương tự nếu tôi gọi đến một phương pháp Java mà ném:

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) 

tôi không thể nói chuyện với gói ngoại lệ Java bên trong một ngoại lệ Java khác từ bên trong C++, mà tôi nghĩ là một phần của câu hỏi của bạn - tôi không thấy cần phải làm điều đó - nhưng nếu tôi làm, tôi sẽ làm điều đó với một trình bao bọc Java xung quanh phương pháp bản địa, hoặc chỉ mở rộng các phương thức ném ngoại lệ của tôi để lấy một jthrowable và thay thế env-> ThrowNew() gọi với một cái gì đó xấu xí: nó không may Sun đã không cung cấp một phiên bản của ThrowNew đã mất một 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); 
} 

Tôi sẽ không xem xét tài liệu tham khảo bộ nhớ đệm (ngoại lệ) constructor lớp vì trường hợp ngoại lệ không được coi là một cơ chế kiểm soát dòng chảy thông thường, vì vậy không nên quan trọng nếu họ chậm. Tôi tưởng tượng nhìn lên không phải là chậm khủng khiếp anyway, kể từ khi Java có lẽ không bộ nhớ đệm của riêng mình cho rằng loại điều.

Các vấn đề liên quan