web-dev-qa-db-ja.com

JNIコードで例外をスローする最良の方法は?

JNIコードで例外をスローする一貫した簡単な方法が欲しい。チェーンされた例外を処理するもの(暗黙的にenv-> ExceptionOccurredメソッドから、または明示的にパラメーターによって、どちらの方法でも良い)、これを行うたびにコンストラクターを検索する必要がありません。上記はすべてCであることが望ましいですが、必要に応じてC++から翻訳することもできます。

SOの誰かが共有できるこのようなものを持っていますか?

55
Chris R

スローする例外のタイプごとにユーティリティメソッドをコーディングするだけです。ここではいくつかの例を示します。

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

そうすれば、それらを簡単に見つけることができます。コード補完エディターを使用すると、入力が簡単になり、簡単なパラメーターを渡すことができます。

これを拡張して、連鎖例外やその他のより複雑なアプローチを処理できると確信しています。これでニーズを十分に満たすことができました。

46

私は単に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.
21
Java42

私のコードは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例外ステータスがエラーを示している場合、すぐに保釈して通常の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)

Java別の内の例外Java C++内からの例外、これはあなたの質問の一部だと思います-私は見つかりませんでしたそれを行う必要があります-しかし、私がした場合、ネイティブメソッドのJavaレベルのラッパーでそれを行うか、jthrowableを取り、env-> ThrowNew()呼び出しを置き換えるために例外スローメソッドを拡張するだけです残念なことに、Sunがjthrowableを使用したThrowNewのバージョンを提供しなかったのは残念です。

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はおそらくそのようなことのために独自のキャッシングを行うので、ルックアップはひどく遅くはないと思います。

6
android.weasel

以前に必要だったように、もう少し説明が必要な人については、より完全で一般的な答えを示します。

まず、メソッドをThrow Exceptionで設定して、IDEがtry/catchを要求します。

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

this のため、IOExceptionよりExceptionを決定します。

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;
}
0
Canato