60 votes

Récupération des exceptions générées par le code natif s'exécutant sur Android

Le projet sur lequel je travaille actuellement sur me demande de code android partie d'une plate-forme de mise en œuvre du programme.

Un ensemble de base de la fonctionnalité est intégrée et inclus dans mon application par le biais d' android-ndk. J'ai trouvé que toute exception/crash qui se passe dans le code natif n'est rapporté ici et là, au mieux. Lorsqu'une erreur se produit-je obtenir l'un des comportements suivants:

  • Une stacktrace / vidage de mémoire se produit et est écrit dans le fichier journal. Le programme disparaît (aucune indication n'est donnée sur le périphérique pourquoi, soudain, l'application n'est plus là).
  • Pas de stacktrace / dump ou une autre indication est donné que le code natif est tombé en panne. Le programme disparaît.
  • Le code java se bloque avec un NullPointerException (en général dans le même endroit par du code natif à l'exception de ce qui est une énorme douleur). Habituellement, m'obligeant à passer du temps à essayer de déboguer pourquoi le code Java a jeté une erreur seulement de découvrir le code Java est fine et le code natif erreur a été entièrement masqué.

Je n'arrive pas à trouver de toute façon à "isoler" mon code à l'encontre des erreurs qui se produisent dans le code natif. Les instructions Try/catch sont amplement ignoré. À part quand mon code est pointé du doigt comme le coupable, je n'ai même pas la chance d'avertir l'utilisateur qu'une erreur s'est produite.

Quelqu'un peut-il m'aider pour savoir comment répondre à la situation de s'écraser en code natif?

59voto

fco.javier.sanz Points 1582

J'ai eu le même problème, il est vrai que dans android (à l'intérieur de toute la VM en général lors de l'exécution de code natif) si vous jetez une des exceptions C++ et celui-ci n'est pas pris, la VM meurt (Si j'ai bien compris, je pense que c'est votre problème). La solution que j'ai adoptée a été d'attraper une exception en C++ et lancer une exception java au lieu de l'utilisation de JNI. Le code suivant est un exemple simplifié de ma solution. Tout d'abord, vous avez un JNI méthode qui attrape l'une des exceptions C++ et ensuite dans l'essai, la clause d'exception Java est annoté.

JNIEXPORT void JNICALL Java_com_MyClass_foo (JNIEnv *env, jobject o,jstring 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("java/lang/RuntimeException");

    if (NULL == c)
    {
        //B plan: null pointer ...
        c = env->FindClass("java/lang/NullPointerException");
    }

    env->ThrowNew(c, msg);
}

Notez qu'après une ThrowNew, la méthode native n'a pas brusquement fin automatiquement. C'est, flux de contrôle renvoie à votre méthode native, et la nouvelle exception est en cours à ce moment. L'exception sera levée après votre JNI méthode est fini.

J'espère que c'était la solution que vous cherchez.

6voto

olibre Points 6069

C'est une alternative basée sur un préprocesseur C macro. Je préfère cette approche. (J'ai utilisé cette astuce dans un JNI implémentation de la couche.)

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

Remplacer mypackage::Exception par vos propres exceptions C++. Si vous n'avez pas défini le correspondant my.group.mypackage.Exception en Java, puis replacez - "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 à l'aide de 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 jstring JNICALL Java_my_group_mypackage_example_function2
  (JNIEnv *env, jobject object, jlong value)
{
  try 
  {
    /* ... my processing ... */
    jstring jstr = env->NewStringUTF("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 l'information ou de la curiosité, je donne ci-dessous le code Java correspondant (fichier example.java). Notez le "my-DLL-name" est au-dessus de code C/C++ compilé sous forme de DLL ("my-DLL-name" sans le ".dll" extension). Cela fonctionne aussi parfaitement à l'utilisation de Linux/Unix bibliothèque partagée *.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 String function2(int); //using the keyword
  private native void   function3(int); //'native'

  public void dosomething(int value) {
    int result = function1(value);  
    String str = function2(value);  //call your DLL functions
    function3(value);               //as any other java function
  }
}

D'abord, générer example.class de example.java (à l'aide d' javac ou votre EDI ou maven...). Deuxièmement, générer des C/C++ fichier d'en-tête Java_my_group_mypackage_example.h de example.class l'aide javah.

0voto

thejartender Points 2704

Avez-vous pensé à la capture de cette exception et puis en l'enveloppant dans une exception d'exécution, juste pour le faire monter plus haut dans la pile?

J'ai utilisé un semblable "hack" dans SCJD. Généralement NPE indique une erreur de votre part, mais si vous êtes convaincu que vous ne faites rien de mal, puis il suffit de faire une bien documenté RuntimeException qui explique que l'exception est utilisé pour la bulle de l'Exception. Ensuite déballer et tester si, par exemple, des entrées en phase nationale et de traiter avec elle en tant que votre propre Exception.

Si c'est le résultat de données erronées, alors vous n'avez pas d'autre option, mais pour arriver à la racine de celui-ci.

-1voto

Jin35 Points 6244

Il est possible d'attraper les exceptions de UncaughtExceptionHandler , mais je ne suis pas sûr s'il va attraper des exceptions du code natif

-2voto

4ntoine Points 1434

Je viens de terminer l'intégration d' ACRA et du bloc de commande : https://github.com/4ntoine/Acra-breakpad

Découvrez la lib et l'application de démonstration. J'espère que cette intégration de deux outils matures sera utile à quelqu'un.

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