61 votes

La meilleure façon de lever des exceptions dans JNI code?

Je voudrais un uniforme et simple de lancer des exceptions dans JNI code; quelque chose qui gère les enchaînés exceptions (implicitement à l'env->ExceptionOccurred méthode, ou explicitement par les paramètres, de toute façon c'est bon) et qui me sauve la recherche de constructeurs chaque fois que je veux faire. Tous les ci-dessus est de préférence en C, bien que je puisse le traduire en C++ au besoin.

Quelqu'un sur DONC avoir quelque chose comme ce qu'ils peuvent partager?

52voto

Steven M. Cherry Points 642

Nous avons juste le code des méthodes d'utilitaire pour chacun des types d'exceptions, nous voulons jeter. Voici quelques exemples:

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

De cette façon, il est facile de les trouver, votre achèvement de code editor vous permet de les saisir, et vous pouvez passer des paramètres simples.

Je suis sûr que vous pourriez développer pour gérer enchaîné les exceptions, ou d'autres plus compliquées, les démarches. Ce fut suffisant pour répondre à nos besoins.

27voto

Java42 Points 4375

J'ai simplement utiliser 2 lignes:

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

Produit:

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

6voto

android.weasel Points 701

Mon code commence en Java, invoque C++, qui invoque ensuite le Java de retour pour des choses comme trouver, obtenir et la définition de valeurs de champ.

Dans le cas où quelqu'un qui recherche un C++ approche trouve cette page, je vais la charrue avec ceci:

Ce que je suis en train de faire maintenant est d'emballage de mon JNI le corps de méthode avec un C++ bloc 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 */
    }
}

où PendingException est déclaré trivialement:

class PendingException {};

et je suis en invoquant la méthode suivante après avoir invoqué tout JNI à partir de C++, donc si l'exception Java statut indique une erreur, je vais caution immédiatement et laissez-la Java normal de gestion des exceptions ajouter le (Native method) de la ligne de la trace de la pile, tout en donnant le C++ la possibilité de nettoyer tout en vous détendant:

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

Mon Java trace de la pile ressemble à ça pour un échec de l'env->GetFieldId() appel:

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)

et un peu comme si j'appelle à une méthode Java qui lance:

 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)

Je ne peux pas parler à entourer l'exception Java au sein d'une autre exception Java à partir de C++, je pense que c'est une partie de votre question - je n'ai pas trouvé la nécessité de le faire - mais si je le faisais, je serais soit le faire avec une classe Java wrapper autour de la native méthodes, ou tout simplement de prolonger mon exception des jets de méthodes pour prendre un jthrowable et remplacer l'env->ThrowNew() avec quelque chose de laid: il est malheureux que le Soleil ne fournit pas une version de ThrowNew qui a eu un 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);
}

Je ne voudrais pas envisager la mise en cache (exception) constructeur de la classe de références car les exceptions ne sont pas censé être une habitude de contrôle de flux de mécanisme, de sorte qu'il ne devrait pas s'ils sont lents. J'imagine look-up n'est pas terriblement lent de toute façon, depuis Java sans doute fait de son propre mise en cache pour ce genre de chose.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X