Cómo capturar excepciones generadas con código nativo que se ejecuta en Android
El proyecto en el que estoy trabajando actualmente requiere que codifique la parte androide de una implementación de programa de plataforma cruzada.
Un conjunto básico de funcionalidad se construye y se incluye en mi aplicación a través de android-ndk
. He encontrado que cualquier excepción / caída que ocurre en el código nativo sólo se informa de vez en cuando en el mejor de los casos. Cuando se produce un error, obtengo uno de los siguientes comportamientos:
- ¿Por qué estoy obteniendo nullpointer en onMeasure en GridView
- NullPointerException en HardwareRenderer
- Mi ubicación actual siempre devuelve null. ¿Cómo puedo arreglar esto?
- No se puede instanciar la base de datos ORM de Sugar
- Android java.lang.NullPointerException: println necesita un mensaje
- Se produce un volcado de pila / memoria y se escribe en el archivo de registro. El programa desaparece (no se indica en el dispositivo por qué de repente la aplicación ya no existe).
- No hay stacktrace / dump u otra indicación es dado que el código nativo se ha estrellado. El programa desaparece.
- El código java se bloquea con una excepción
NullPointerException
(generalmente en el mismo lugar por excepción de código nativo que es un dolor masivo). Por lo general, hacerme pasar bastante tiempo tratando de depurar por qué el código Java ha lanzado un error sólo para descubrir el código de Java está bien y el error de código nativo ha sido totalmente enmascarado.
No puedo encontrar ninguna manera de "aislar" mi código contra los errores que se producen en el código nativo. Las sentencias Try / catch se ignoran rotundamente. Aparte de cuando mi código es fingered como el culpable ni siquiera tengo la oportunidad de advertir al usuario que se ha producido un error.
¿Puede alguien por favor me ayude en cuanto a cómo responder a la situación de fallar código nativo?
- Cómo agregar un SearchWidget a la ActionBar?
- Android NullPointerException en Instrumentation.execStartActivity
- ¿Por qué se arroja NPE al finalizar Actividad?
- Java.lang.RuntimeException: Error al entregar el resultado ResultInfo {who = null, request = 1888, resultado = 0, data = null} a la actividad
- Excepción de Android asynctask
- - java.lang.NullPointerException - setText en la referencia de objeto nulo
- MapFragment que causa NullPointerException en el método getMapAsync (this)
- Android Fragment getActivity () = nulo
Yo solía tener el mismo problema, es cierto que en android (dentro de cualquier VM en general, al ejecutar código nativo) si se lanza una excepción de C + + y este no se captura, la VM muere (Si he entendido correctamente, creo que es tu problema). La solución que adopté fue capturar cualquier excepción en C + + y lanzar una excepción java en lugar de usar JNI. El siguiente código es un ejemplo simplificado de mi solución. En primer lugar, tiene un método JNI que detecta una excepción de C ++ y, a continuación, en la cláusula try se anota la excepción de 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("java/lang/RuntimeException"); if (NULL == c) { //B plan: null pointer ... c = env->FindClass("java/lang/NullPointerException"); } env->ThrowNew(c, msg); }
Tenga en cuenta que después de un ThrowNew, el método nativo no termina de forma abrupta automáticamente. Es decir, el flujo de control vuelve a su método nativo y la nueva excepción está pendiente en este momento. La excepción se lanzará después de que su método JNI esté terminado.
Espero que sea la solución que buscas.
EDIT: Véase también esta respuesta más elegante .
Debajo del mecanismo se basa en una macro de preprocesador C que he implementado con éxito dentro de una capa JNI .
La macro anterior CATCH_CPP_EXCEPTION_AND_THROW_JAVA_EXCEPTION
convierte las excepciones de C ++ en excepciones de Java.
Reemplace mypackage::Exception
por su propia excepción de C ++. Si no ha definido el my.group.mypackage.Exception
correspondiente en Java, reemplace "my/group/mypackage/Exception"
por "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"); \ }
El archivo Java_my_group_mypackage_example.cpp
utiliza la macro anterior:
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 }
Solo por información o curiosidad, proveo debajo del código Java correspondiente (archivo example.java
). Tenga en cuenta el "nombre de my-DLL-name
" es el código C / C ++ anterior compilado como un DLL (" my-DLL-name
" sin la extensión " .dll
"). Esto también funciona perfectamente con Linux / Unix biblioteca compartida *.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 } }
Primero, genera example.class
desde example.java
(usando javac
o tu IDE favorito o maven …). Segundo, genere el archivo de encabezado C / C ++ Java_my_group_mypackage_example.h
desde example.class
usando javah
.
¿Ha considerado capturar esta excepción y luego envolverla en una excepción de ejecución, sólo para subirla más arriba en la pila?
Utilicé un "hack" similar en SCJD. En general, NPE indica un error de su parte, pero si está convencido de que no está haciendo nada malo, simplemente haga una RuntimeException
bien documentada que explique que la excepción se utiliza para burbujear la excepción. A continuación, desenrollarlo y probar si por ejemplo de NPE y tratar con él como su propia Excepción.
Si va a resultar en datos erróneos, entonces no tienes otra opción, pero para llegar a la raíz de la misma.
Existe la posibilidad de capturar excepciones por UncaughtExceptionHandler
, pero no estoy seguro de si capturará excepciones de código nativo
Acabo de terminar ACRA y la integración de breakpad : https://github.com/4ntoine/Acra-breakpad
Echa un vistazo a la lib y demo de la aplicación. Espero que esta integración de dos herramientas maduras será útil para alguien.
- Environment.getExternalStorageDirectory no devuelve la ruta al almacenamiento extraíble
- Google Play Services 5.0.77