捕获在Android上运行的本机代码引发的exception

我目前正在进行的项目需要我编写跨平台程序实现的android部分。

我的应用程序通过android-ndk构build了一套核心function。 我发现,在本地代码中发生的任何exception/崩溃只能一次又一次地报告。 发生错误时,出现以下某种行为:

  • 发生堆栈跟踪/内存转储并写入日志文件。 该程序消失(没有迹象表明,为什么突然,应用程序不再在那里)。
  • 没有堆栈跟踪/转储或其他指示,本机代码崩溃。 程序消失。
  • java代码崩溃,出现NullPointerExceptionexception(通常在每个本地代码exception的地方,这是一个巨大的痛苦)。 通常会导致我花费相当长的时间来debugging为什么Java代码抛出一个错误只发现Java代码是好的,本机代码错误已被完全掩盖。

我似乎无法find任何方法来“绝缘”我的代码与本地代码中发生的错误。 Try / catch语句被完全忽略。 除了当我的代码被指责为罪魁祸首,我甚至没有机会警告用户,而不是发生错误。

有人可以帮我解决如何应对崩溃的本地代码的情况?

我曾经有过同样的问题,如果抛出一个C ++exception,并且没有捕获到这个exception,那么在android中(在执行本机代码的任何虚拟机内部)都是这样的,虚拟机死了(如果我理解正确的话,是你的问题)。 我采用的解决scheme是在C ++中捕获任何exception,并抛出一个javaexception,而不是使用JNI。 下一个代码是我解决scheme的一个简单例子。 首先你有一个JNI方法捕获一个C ++exception,然后在try子句中注释Javaexception。

 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之后,本地方法不会突然自动终止。 也就是说,控制stream将返回到您的本地方法,并且此时新的exception处于挂起状态。 JNI方法完成后,将抛出exception。

我希望这是你正在寻找的解决scheme。

编辑:另请参阅这个更优雅的答案 。


下面的机制是基于一个C预处理macros ,我已经成功地在一个JNI层实现。

上面的macrosCATCH_CPP_EXCEPTION_AND_THROW_JAVA_EXCEPTION将C ++exception转换成Javaexception。

用你自己的C ++exceptionreplacemypackage::Exception Exception。 如果您没有在Java中定义相应的my.group.mypackage.Exception ,则用"java/lang/RuntimeException"replace"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"); \ } 

使用上述macros的文件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 ”是上述编译为DLL(“ my-DLL-name ”,不带“ .dll ”扩展名)的C / C ++代码。 这也可以完美的使用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.java生成example.class (使用javac或您最喜欢的IDE或maven …)。 其次,使用javahexample.class生成C / C ++头文件Java_my_group_mypackage_example.h

你有没有考虑捕获这个exception,然后把它包装在一个运行时exception,只是为了得到它在堆栈中更高?

我在SCJD使用了类似的“黑客”。 一般来说NPE表示你的错误,但是如果你确信你没有做错什么,那么只要做一个logging良好的RuntimeException来解释这个exception是用来冒泡Exception。 然后打开它并testing是否NPE的例子,并作为你自己的例外处理。

如果它会导致错误的数据,那么你没有别的select,但要find它的根源。