Catching exceptions levées à partir du code natif fonctionnant sur Android

Le projet sur lequel je travaille actuellement nécessite de coder la partie Android d’une implémentation de programme multi-plateforme.

Un ensemble de fonctionnalités de base est créé et inclus dans mon application via android-ndk . J’ai trouvé que toute exception / plantage qui se produit dans le code natif n’est signalée que de temps en temps. Lorsqu’une erreur survient, j’obtiens l’un des comportements suivants:

  • Un vidage de stack / trace se produit et est écrit dans le fichier journal. Le programme disparaît (aucune indication n’est donnée sur l’appareil pour savoir si soudainement l’application n’est plus là).
  • Aucun stacktrace / dump ou autre indication n’est donné que le code natif est tombé en panne. Le programme disparaît.
  • Le code java se bloque avec une exception NullPointerException (généralement au même endroit par exception de code natif, ce qui est très pénible). En général, le fait que je passe beaucoup de temps à essayer de déboguer pourquoi le code Java a généré une erreur uniquement pour découvrir que le code Java fonctionne correctement et que l’erreur de code native a été entièrement masquée.

Je n’arrive pas à trouver un moyen d’isoler mon code contre les erreurs qui surviennent dans le code natif. Les instructions try / catch sont ignorées. Mis à part le fait que mon code soit accusé, je n’ai même pas la possibilité d’avertir l’utilisateur qu’une erreur s’est produite.

Quelqu’un peut-il m’aider s’il vous plaît comment répondre à la situation de plantage de code natif?

J’avais l’habitude d’avoir le même problème, il est vrai que dans Android (dans n’importe quelle VM en général lors de l’exécution de code natif) si vous lancez une exception C ++ et que celle-ci n’est pas interceptée, la VM meurt (si j’ai bien compris, Est votre problème). La solution que j’ai adoptée consistait à intercepter toute exception en C ++ et à lancer une exception java au lieu d’utiliser JNI. Le code suivant est un exemple simplifié de ma solution. Tout d’abord, vous avez une méthode JNI qui intercepte une exception C ++, puis dans la clause try l’annotation Java.

 JNIEXPORT void JNICALL Java_com_MyClass_foo (JNIEnv *env, jobject o,jssortingng param) { try { // Your Stuff ... } // You can catch std::exception for more generic error handling catch (MyCxxException e) { throwJavaException (env, e.what()); } } void throwJavaException(JNIEnv *env, const char *msg) { // You can put your own exception here jclass c = env->FindClass("company/com/YourException"); if (NULL == c) { //B plan: null pointer ... c = env->FindClass("java/lang/NullPointerException"); } env->ThrowNew(c, msg); } 

Notez qu’après un ThrowNew, la méthode native ne se termine pas automatiquement. En d’autres termes, le stream de contrôle retourne à votre méthode native et la nouvelle exception est en attente à ce stade. L’exception sera lancée une fois votre méthode JNI terminée.

J’espère que c’est la solution que vous recherchez.

EDIT: Voir aussi cette réponse plus élégante .


Le mécanisme ci-dessous est basé sur une macro de préprocesseur C que j’ai implémentée avec succès dans une couche JNI .

La macro ci-dessus CATCH_CPP_EXCEPTION_AND_THROW_JAVA_EXCEPTION convertit les exceptions C ++ en exceptions Java.

Remplacez mypackage::Exception par votre propre exception C ++. Si vous n’avez pas défini le my.group.mypackage.Exception correspondant en Java, remplacez "my/group/mypackage/Exception" par "java/lang/RuntimeException" .

 #define CATCH_CPP_EXCEPTION_AND_THROW_JAVA_EXCEPTION \ \ catch (const mypackage::Exception& e) \ { \ jclass jc = env->FindClass("my/group/mypackage/Exception"); \ if(jc) env->ThrowNew (jc, e.what()); \ /* if null => NoClassDefFoundError already thrown */ \ } \ catch (const std::bad_alloc& e) \ { \ /* OOM exception */ \ jclass jc = env->FindClass("java/lang/OutOfMemoryError"); \ if(jc) env->ThrowNew (jc, e.what()); \ } \ catch (const std::ios_base::failure& e) \ { \ /* IO exception */ \ jclass jc = env->FindClass("java/io/IOException"); \ if(jc) env->ThrowNew (jc, e.what()); \ } \ catch (const std::exception& e) \ { \ /* unknown exception */ \ jclass jc = env->FindClass("java/lang/Error"); \ if(jc) env->ThrowNew (jc, e.what()); \ } \ catch (...) \ { \ /* Oops I missed identifying this exception! */ \ jclass jc = env->FindClass("java/lang/Error"); \ if(jc) env->ThrowNew (jc, "unidentified exception"); \ } 

Le fichier Java_my_group_mypackage_example.cpp utilisant la macro ci-dessus:

 JNIEXPORT jlong JNICALL Java_my_group_mypackage_example_function1 (JNIEnv *env, jobject object, jlong value) { try { /* ... my processing ... */ return jlong(result); } CATCH_CPP_EXCEPTION_AND_THROW_JAVA_EXCEPTION return 0; } JNIEXPORT jssortingng JNICALL Java_my_group_mypackage_example_function2 (JNIEnv *env, jobject object, jlong value) { try { /* ... my processing ... */ jssortingng jstr = env->NewSsortingngUTF("my result"); return jstr; } CATCH_CPP_EXCEPTION_AND_THROW_JAVA_EXCEPTION return 0; } JNIEXPORT void JNICALL Java_my_group_mypackage_example_function3 (JNIEnv *env, jobject object, jlong value) { try { /* ... my processing ... */ } CATCH_CPP_EXCEPTION_AND_THROW_JAVA_EXCEPTION } 

Juste pour information ou curiosité, je fournis ci-dessous le code Java correspondant (fichier example.java ). Notez que ” my-DLL-name ” est le code C / C ++ ci-dessus compilé en tant que DLL (” my-DLL-name ” sans l’extension ” .dll “). Cela fonctionne également parfaitement en utilisant la bibliothèque partagée Linux / Unix *.so .

 package my.group.mypackage; public class Example { static { System.loadLibrary("my-DLL-name"); } public Example() { /* ... */ } private native int function1(int); //declare DLL functions private native Ssortingng function2(int); //using the keyword private native void function3(int); //'native' public void dosomething(int value) { int result = function1(value); Ssortingng str = function2(value); //call your DLL functions function3(value); //as any other java function } } 

Tout d’abord, générez example.class partir de example.java (en utilisant javac ou votre IDE ou maven préféré …). Deuxièmement, générez le fichier d’en-tête C / C ++ Java_my_group_mypackage_example.h partir de example.class utilisant javah .

Avez-vous envisagé d’attraper cette exception puis de l’enrouler dans une exception d’exécution, simplement pour la monter plus haut dans la stack?

J’ai utilisé un “hack” similaire dans SCJD. En général, NPE indique une erreur de votre part, mais si vous êtes convaincu que vous ne faites rien de mal, faites simplement une RuntimeException bien documentée qui explique que l’exception est utilisée pour créer une bulle. Déroulez-le ensuite et testez si, par exemple, NPE et traitez-le comme votre propre exception.

Si cela se traduit par des données erronées, alors vous n’avez pas d’autre choix que d’aller à la racine.