捕获在Android上运行的本机代码引发的exception
我目前正在进行的项目需要我编写跨平台程序实现的android部分。
我的应用程序通过android-ndk
构build了一套核心function。 我发现,在本地代码中发生的任何exception/崩溃只能一次又一次地报告。 发生错误时,出现以下某种行为:
- 发生堆栈跟踪/内存转储并写入日志文件。 该程序消失(没有迹象表明,为什么突然,应用程序不再在那里)。
- 没有堆栈跟踪/转储或其他指示,本机代码崩溃。 程序消失。
- java代码崩溃,出现
NullPointerException
exception(通常在每个本地代码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 …)。 其次,使用javah
从example.class
生成C / C ++头文件Java_my_group_mypackage_example.h
。
你有没有考虑捕获这个exception,然后把它包装在一个运行时exception,只是为了得到它在堆栈中更高?
我在SCJD使用了类似的“黑客”。 一般来说NPE表示你的错误,但是如果你确信你没有做错什么,那么只要做一个logging良好的RuntimeException
来解释这个exception是用来冒泡Exception。 然后打开它并testing是否NPE的例子,并作为你自己的例外处理。
如果它会导致错误的数据,那么你没有别的select,但要find它的根源。