Лучший способ бросить исключения в коде JNI? - PullRequest
54 голосов
/ 23 октября 2008

Мне нужен последовательный и простой способ генерировать исключения в коде JNI; что-то, что обрабатывает цепочечные исключения (неявно из метода env-> ExceptionOccurred или явно по параметрам, в любом случае это хорошо) и избавляет меня от поиска конструкторов каждый раз, когда я хочу это сделать. Все вышеперечисленное предпочтительно на C, хотя я мог бы перевести его с C ++ при необходимости.

У кого-нибудь на SO есть что-то подобное, чем он может поделиться?

Ответы [ 4 ]

45 голосов
/ 27 октября 2008

Мы просто кодируем служебные методы для каждого из типов исключений, которые мы хотим выбросить. Вот несколько примеров:

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

Таким образом, их легко найти, ваш редактор дополнения кода поможет вам ввести их, и вы можете передать простые параметры.

Я уверен, что вы могли бы расширить это для обработки связанных исключений или других более сложных подходов. Этого было достаточно для удовлетворения наших потребностей.

21 голосов
/ 22 марта 2012

Я просто использую 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.
6 голосов
/ 31 августа 2012

Мой код запускается на 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 {};

и я вызываю следующий метод после вызова любого JNI из C ++, поэтому, если состояние исключения Java указывает на ошибку, я немедленно освобождаюсь от обязательств и позволяю обычной обработке исключений Java добавить строку (метод Native) в стек трассировки, давая 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 не предоставила версию ThrowNew это заняло jrowrowable.

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 для этой вещи выполняет собственное кэширование.

0 голосов
/ 28 декабря 2018

Я поставлю более полный и общий ответ для тех, кому нужно немного больше объяснений, как мне нужно раньше.

Во-первых, неплохо установить ваш метод с помощью 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;
}
...