Wyłapywanie WYJĄTKÓW wyrzucanych z kodu natywnego działającego na Androidzie

Projekt, nad którym obecnie pracuję, wymaga ode mnie zakodowania android części wieloplatformowej implementacji programu.

Podstawowy zestaw funkcjonalności jest zbudowany i zawarty w mojej aplikacji poprzez android-ndk. Odkryłem, że każdy wyjątek / awaria, która dzieje się w kodzie macierzystym jest zgłaszany tylko teraz i znowu w najlepszym razie. Gdy wystąpi błąd, otrzymuję jedno z następujących zachowań:

  • występuje stacktrace / zrzut pamięci i jest zapisywany do pliku dziennika. Program znika (nie na urządzeniu znajduje się wskazanie, dlaczego nagle aplikacja nie istnieje).
  • nie podano stacktrace / dump ani innych wskazań, że natywny kod uległ awarii. Program znika.
  • kod Javy ulega awarii z NullPointerException (zwykle w tym samym miejscu na wyjątek kodu natywnego, co jest ogromnym bólem). Zwykle powoduje, że spędzam sporo czasu próbując debugować, dlaczego kod Java wyrzucił błąd, aby odkryć, że kod Java jest w porządku i błąd kodu macierzystego został całkowicie zamaskowany.

Nie mogę znaleźć sposobu na "izolowanie" mojego kodu przed błędami, które występują w kodzie natywnym. Instrukcje Try/catch są wyraźnie ignorowane. Poza tym, gdy mój kod jest fingered jako winowajca nie mam nawet szansę ostrzec użytkownika, niż wystąpił błąd.

Czy ktoś może mi pomóc jak zareagować na sytuację awarii natywnego kodu?

Author: Graeme, 2011-12-07

3 answers

Kiedyś miałem ten sam problem, to prawda, że w Androidzie (w każdej maszynie wirtualnej w ogóle podczas wykonywania kodu natywnego) jeśli rzucisz wyjątek C++ i ten nie zostanie złapany, maszyna wirtualna umrze (jeśli dobrze zrozumiałem, myślę, że to twój problem). Rozwiązaniem, które przyjąłem, było złapanie jakiegokolwiek wyjątku w C++ i rzucenie wyjątku w Javie zamiast używania JNI. Następny kod to uproszczony przykład mojego rozwiązania. Przede wszystkim masz metodę JNI, która łapie wyjątek C++, a następnie w próbie klauzula wyjątek Java jest adnotowany.

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("company/com/YourException");

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

    env->ThrowNew(c, msg);
}

Zauważ, że po rzuceniu nowej, natywna metoda nie kończy się nagle automatycznie. Oznacza to, że przepływ sterowania powraca do natywnej metody, a nowy wyjątek jest w oczekiwaniu na ten moment. Wyjątek zostanie wyrzucony po zakończeniu metody JNI.

Mam nadzieję, że to było rozwiązanie, którego szukasz.

 42
Author: javier-sanz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-10-26 09:26:38

EDIT: Zobacz także ta bardziej elegancka odpowiedź.


Poniższy mechanizm oparty jest na makrze preprocesora C, które z powodzeniem zaimplementowałem w warstwie JNI.

Powyższe makro CATCH_CPP_EXCEPTION_AND_THROW_JAVA_EXCEPTION konwertuje wyjątki C++ na wyjątki Javy.

Zastąp mypackage::Exception przez własny wyjątek C++. Jeśli nie zdefiniowano odpowiedniej my.group.mypackage.Exception w Javie, zastąp "my/group/mypackage/Exception" przez "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");          \
  }

Plik Java_my_group_mypackage_example.cpp za pomocą powyższe makro:

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
}

Dla informacji lub ciekawości podaję poniżej odpowiedni kod Javy (plik example.java). Zauważ, że" my-DLL-name " jest powyższym kodem C / C++ skompilowanym jako DLL ("my-DLL-name "bez rozszerzenia" .dll"). Działa to również doskonale przy użyciu biblioteki współdzielonej 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 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
  }
}

Najpierw Wygeneruj example.class z example.java (używając javaclub swojego ulubionego IDE lub Mavena...). Po drugie, Wygeneruj plik nagłówkowy C / C++ Java_my_group_mypackage_example.h z example.class za pomocą javah.

 5
Author: olibre,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 10:30:59

Czy rozważałeś przechwycenie tego wyjątku, a następnie owinięcie go w wyjątek runtime, tylko po to, aby podnieść go wyżej w stosie?

Użyłem podobnego "hacka" w SCJD. Ogólnie NPE wskazuje na błąd z twojej strony, ale jeśli jesteś przekonany, że nie robisz nic złego, po prostu zrób dobrze udokumentowaną RuntimeException, która wyjaśnia, że wyjątek jest używany do bańki wyjątku. Następnie rozpakuj go i przetestuj, czy na przykład NPE i potraktuj go jako swój własny wyjątek.

Jeśli jest zamierzając spowodować błędne dane, nie masz innej opcji, ale dostać się do źródła.

 0
Author: thejartender,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-12-12 09:57:28