JNIコードで例外をスローする最良の方法は?
-
04-07-2019 - |
質問
JNIコードで例外をスローする一貫した簡単な方法が欲しい。連鎖例外を処理するもの(暗黙的にenv-> ExceptionOccurredメソッドから、または明示的にパラメーターによって、どちらの方法でも良い)、これを行うたびにコンストラクターを検索する必要がありません。必要に応じてC ++から翻訳することもできますが、上記はすべてCであることが望ましいです。
SOの誰かが共有できるこのようなものを持っていますか?
解決
スローする例外の種類ごとにユーティリティメソッドをコーディングします。以下に例を示します。
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 );
}
そのように、それらを見つけるのは簡単です。コード補完エディタはそれらを入力するのを助け、簡単なパラメータを渡すことができます。
これを拡張して、連鎖例外やその他のより複雑なアプローチを処理できると確信しています。これでニーズを十分に満たすことができました。
他のヒント
単純に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.
私のコードはJavaで始まり、C ++を呼び出します。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;
}
}
失敗したenv-> GetFieldId()呼び出しの場合、私のJavaスタックトレースは次のようになります。
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例外をC ++内から別のJava例外内にラップすることはできません。これはあなたの質問の一部だと思います。ネイティブメソッドをJavaレベルのラッパーで実行するか、例外をスローするメソッドを拡張してjthrowableを取得し、env-> ThrowNew()呼び出しをsomethingいものに置き換えます。残念なことに、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はおそらくそのようなことに対して独自のキャッシングを行うため、ルックアップがとにかく遅くなることはないと思います。
以前に必要だったように、もう少し説明が必要な人には、より完全で一般的な答えを出します。
最初に Throw Exception
でメソッドを設定すると、IDEがtry / catchを要求します。
public native int func(Param1, Param2, Param3) throws IOException;
これのため、 Exception
よりも IOException
を決定します。
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;
}