Исключение исключений из собственного кода, запущенного на Android
Проект, над которым я сейчас работаю, требует от меня кодирования части андроида реализации кросс-платформенной программы.
Основной набор функций встроен и включен в мое приложение через android-ndk
. Я обнаружил, что любое исключение/сбой, которое происходит в собственном коде, сообщается только сейчас и снова в лучшем случае. Когда возникает ошибка, я получаю одно из следующих действий:
- Выполняется сценарий stacktrace/memory и записывается в файл журнала. Программа исчезает (на устройстве не указывается, почему приложение больше не существует).
- Никакой stacktrace/дамп или другое указание не указано, что исходный код разбился. Программа исчезает.
- Сбой java-кода с помощью
NullPointerException
(обычно в том же месте, что и в случае с внутренним кодом, которое является огромной болью). Обычно заставляя меня тратить довольно много времени, пытаясь отлаживать, почему код Java породил ошибку, только для того, чтобы обнаружить Java-код, прекрасно, а ошибка внутреннего кода была полностью замаскирована.
Я не могу найти способ "изолировать" мой код от ошибок, которые происходят в собственном коде. Заявления Try/catch звучат без внимания. Кроме того, когда мой код попал в качестве виновника, я даже не получаю возможность предупредить пользователя, чем произошла ошибка.
Может кто-нибудь, пожалуйста, помогите мне в том, как реагировать на ситуацию с сбоем собственного кода?
Ответы
Ответ 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, и рассматривайте его как свое собственное исключение.
Если это приведет к ошибочным данным, тогда у вас нет другого выбора, кроме как добраться до его корня.