Подтвердить что ты не робот

Исключение исключений из собственного кода, запущенного на Android

Проект, над которым я сейчас работаю, требует от меня кодирования части андроида реализации кросс-платформенной программы.

Основной набор функций встроен и включен в мое приложение через android-ndk. Я обнаружил, что любое исключение/сбой, которое происходит в собственном коде, сообщается только сейчас и снова в лучшем случае. Когда возникает ошибка, я получаю одно из следующих действий:

  • Выполняется сценарий stacktrace/memory и записывается в файл журнала. Программа исчезает (на устройстве не указывается, почему приложение больше не существует).
  • Никакой stacktrace/дамп или другое указание не указано, что исходный код разбился. Программа исчезает.
  • Сбой java-кода с помощью NullPointerException (обычно в том же месте, что и в случае с внутренним кодом, которое является огромной болью). Обычно заставляя меня тратить довольно много времени, пытаясь отлаживать, почему код Java породил ошибку, только для того, чтобы обнаружить Java-код, прекрасно, а ошибка внутреннего кода была полностью замаскирована.

Я не могу найти способ "изолировать" мой код от ошибок, которые происходят в собственном коде. Заявления Try/catch звучат без внимания. Кроме того, когда мой код попал в качестве виновника, я даже не получаю возможность предупредить пользователя, чем произошла ошибка.

Может кто-нибудь, пожалуйста, помогите мне в том, как реагировать на ситуацию с сбоем собственного кода?

4b9b3361

Ответ 1

У меня была такая же проблема, правда, что в android (внутри любой виртуальной машины вообще при выполнении собственного кода), если вы бросаете исключение С++, и этот не пойман, VM умирает (если я правильно понял, Я думаю, что это ваша проблема). Решение, которое я принял, состояло в том, чтобы поймать любое исключение в С++ и выбросить исключение java вместо использования JNI. Следующий код - упрощенный пример моего решения. Прежде всего, у вас есть метод JNI, который ловит исключение С++, а затем в предложении try исключение Java аннотируется.

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

Обратите внимание, что после ThrowNew собственный метод не прерывается автоматически. То есть поток управления возвращается к вашему собственному методу, и новое исключение находится на рассмотрении в данный момент. Исключение будет выбрано после завершения вашего метода JNI.

Надеюсь, это решение, которое вы ищете.

Ответ 2

EDIT: & ensp; См. Также этот более элегантный ответ.


Ниже механизм основан на макрос препроцессора C, который я успешно реализовал в JNI.

Вышеуказанный макрос CATCH_CPP_EXCEPTION_AND_THROW_JAVA_EXCEPTION преобразует исключения С++ в исключения Java.

Замените mypackage::Exception на свой собственный С++ Exception. Если вы не определили соответствующий my.group.mypackage.Exception в Java, замените "my/group/mypackage/Exception" на "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");          \
  }

Файл Java_my_group_mypackage_example.cpp с использованием макроса выше:

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
}

Просто для информации или любопытства я предоставляю ниже соответствующий Java-код (файл example.java). Обратите внимание: "my-DLL-name" - это код C/С++, скомпилированный как DLL ( "my-DLL-name" без расширения ".dll" ). Это также отлично работает с использованием общей библиотеки 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
  }
}

Сначала сгенерируйте example.class из example.java (используя javac или ваш любимый IDE или Maven...). Во-вторых, сгенерируйте заголовочный файл C/С++ Java_my_group_mypackage_example.h из example.class с помощью javah.

Ответ 3

Считаете ли вы, что вы поймаете это исключение и затем оберните его в исключение для выполнения, просто чтобы получить его выше в стеке?

Я использовал подобный "взлом" в SCJD. Обычно NPE указывает на ошибку с вашей стороны, но если вы уверены, что не делаете ничего плохого, просто сделайте хорошо документированный RuntimeException, объясняющий, что это исключение используется для создания пузырьков Исключения. Затем разверните его и проверьте, если, например, NPE, и рассматривайте его как свое собственное исключение.

Если это приведет к ошибочным данным, тогда у вас нет другого выбора, кроме как добраться до его корня.