programing

JNI 코드에서 예외를 발생시키는 가장 좋은 방법은 무엇입니까?

nasanasas 2021. 1. 8. 08:19
반응형

JNI 코드에서 예외를 발생시키는 가장 좋은 방법은 무엇입니까?


JNI 코드에서 예외를 발생시키는 일관되고 간단한 방법을 원합니다. 체인 된 예외를 처리하고 (env-> ExceptionOccurred 메서드에서 암시 적으로 또는 매개 변수에 의해 명시 적으로 지정하는 것이 좋습니다.)이 작업을 수행 할 때마다 생성자를 찾는 데 도움이됩니다. 필요한 경우 C ++에서 번역 할 수 있지만 위의 모든 내용은 바람직하게는 C로되어 있습니다.

그래서 누구든지 공유 할 수있는 이와 같은 것을 가지고 있습니까?


우리는 던지기를 원하는 각 예외 유형에 대한 유틸리티 메서드를 코딩합니다. 여기 예시들이 있습니다 :

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

이렇게하면 쉽게 찾을 수 있고 코드 완성 편집기에서 입력하는 데 도움이되고 간단한 매개 변수를 전달할 수 있습니다.

이 기능을 확장하여 연결된 예외 또는 기타 더 복잡한 접근 방식을 처리 할 수 ​​있다고 확신합니다. 이것은 우리의 필요를 충족시키기에 충분했습니다.


I simply use 2 lines:

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

Produces:

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

My code starts in Java, invokes C++, which then invokes Java back again for things like finding, getting, and setting field values.

In case someone looking for a C++ approach finds this page, I'll plough on with this:

What I'm now doing is wrapping my JNI method bodies up with a 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 */
    }
}

where PendingException is declared trivially:

class PendingException {};

and I'm invoking the following method after invoking any JNI from C++, so if the Java exception status indicates an error, I'll bail immediately and let the normal Java exception handling add the (Native method) line to the stack trace, while giving the C++ the opportunity to clean up while unwinding:

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

My Java stack trace looks like this for a failed env->GetFieldId() call:

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)

and pretty similar if I call up to a Java method that throws:

 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)

I can't talk to wrapping the Java exception within another Java exception from within C++, which I think is part of your question - I've not found the need to do that - but if I did, I'd either do it with a Java-level wrapper around the native methods, or just extend my exception-throwing methods to take a jthrowable and replace the env->ThrowNew() call with something ugly: it's unfortunate Sun didn't provide a version of ThrowNew that took a 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);
}

I wouldn't consider caching (exception) class constructor references because exceptions aren't supposed to be a usual control flow mechanism, so it shouldn't matter if they're slow. I imagine look-up isn't terribly slow anyway, since Java presumably does its own caching for that sort of thing.


I will put a more complete and general answer for who need a little bit more explanations like I need before.

First is nice to set your method with a Throw Exception so the IDE will ask for try/catch.

public native int func(Param1, Param2, Param3) throws IOException;

I decide for IOException over Exception because of this.

JNIEXPORT int JNICALL Java_YourClass_func
(int Param1, int Param2, int Param3) {
    if (Param3 == 0) { //something wrong
        jclass Exception = env->FindClass("java/lang/Exception");
        env->ThrowNew(Exception, "Can't divide by zero."); // Error Message
    }
    return (Param1+Param2)/Param3;
}

ReferenceURL : https://stackoverflow.com/questions/230689/best-way-to-throw-exceptions-in-jni-code

반응형