Лучший способ бросить исключения в код JNI?
Мне нужен последовательный и простой способ бросить исключения в коде JNI; то, что обрабатывает замкнутые исключения (неявно из метода env- > ExceptionOccurred или явно с помощью параметров, в любом случае это хорошо) и сохраняет меня при поиске конструкторов каждый раз, когда я хочу это сделать. Все вышеперечисленное предпочтительно находится в C, хотя я мог бы перевести его из С++ при необходимости.
Есть ли у кого-нибудь из них что-то подобное, что они могут делиться?
Ответы
Ответ 1
Мы просто используем утилиты для каждого из типов исключений, которые мы хотим бросить. Вот несколько примеров:
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
Я просто использую 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.
Ответ 3
Мой код запускается в Java, вызывает С++, который затем вызывает Java снова для таких вещей, как поиск, получение и установка значений полей.
Если кто-то ищет подход на С++, найдет эту страницу, я буду пахать с этим:
То, что я сейчас делаю, заключается в том, чтобы обернуть мои тела методов JNI блоком 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 {};
и я вызываю следующий метод после вызова любого JNI из С++, поэтому, если статус исключения Java указывает на ошибку, я немедленно заступлю и позволяю нормальной обработке исключений Java добавлять строку (Native method) в стек trace, давая С++ возможность очистки во время размотки:
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 из С++, который, я думаю, является частью вашего вопроса - я не нашел нужды в этом, но если бы я это сделал, я бы либо сделайте это с помощью обертки на уровне Java вокруг собственных методов или просто продолжите мои методы исключения, чтобы сделать jthrowable и замените вызов env- > ThrowNew() чем-то уродливым: это неудачное Sun не предоставило версию ThrowNew который взял 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);
}
Я бы не рассматривал ссылки на конструкторы классов кеширования (исключения), потому что исключения не должны быть обычным механизмом управления потоком, поэтому не имеет значения, медленны ли они. Я предполагаю, что в любом случае внешний вид не слишком медленный, так как Java, по-видимому, делает свое собственное кэширование для такого рода вещей.
Ответ 4
Я поставлю более полный и общий ответ для тех, кому нужно немного больше объяснений, как мне нужно раньше.
Во-первых, неплохо установить ваш метод с Throw Exception
чтобы IDE запросила try/catch.
public native int func(Param1, Param2, Param3) throws IOException;
Я решаю для 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;
}