我目前正在进行的项目要求我编写跨平台程序实现的android部分。
构建了一组核心功能,并通过android-ndk
将其包含在我的应用中。我发现在本机代码中发生的任何异常/崩溃只会在现在和最后再次报告。发生错误时,我会收到以下行为之一:
NullPointerException
崩溃(通常在每个本机代码异常的相同位置,这是一个巨大的痛苦)。通常会让我花费很长时间来尝试调试为什么Java代码只发出错误才发现Java代码很好&本机代码错误已被完全屏蔽。我似乎无法找到任何方法来“隔离”我的代码以防止在本机代码中出现的错误。 Try / catch语句被彻底忽略了。除了我的代码被指责为罪魁祸首之外,我甚至没有机会警告用户而不是发生错误。
有人可以帮助我解决如何应对崩溃本机代码的情况吗?
答案 0 :(得分:47)
我曾经有同样的问题,确实在android(在执行本机代码时通常在任何VM中)如果你抛出一个C ++异常并且没有捕获到这个,VM就会死掉(如果我理解正确,我认为这是你的问题)。我采用的解决方案是捕获C ++中的任何异常并抛出java异常而不是使用JNI。下一个代码是我解决方案的简化示例。首先,您有一个捕获C ++异常的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方法完成后将抛出异常。
我希望这是您正在寻找的解决方案。
答案 1 :(得分:5)
编辑:另请参阅this more elegant answer。
以下机制基于我在C preprocessor macro图层内成功实施的JNI。
上面的宏CATCH_CPP_EXCEPTION_AND_THROW_JAVA_EXCEPTION
将C ++异常转换为Java异常。
用您自己的C ++异常替换mypackage::Exception
。如果您没有在Java中定义相应的my.group.mypackage.Exception
,请将"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
”是编译为DLL的上述C / C ++代码(“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 ...)。其次,使用Java_my_group_mypackage_example.h
从example.class
生成C / C ++头文件javah
。
答案 2 :(得分:0)
您是否考虑过捕获此异常然后将其包装在运行时异常中,只是为了让它在堆栈中更高?
我在SCJD中使用了类似的'hack'。一般来说,NPE表示您的错误,但如果您确信自己没有做错任何事情,那么只需记录好RuntimeException
即可解释异常用于冒泡异常。然后打开它并测试是否为NPE的例子并将其作为您自己的例外处理。
如果它会导致错误的数据,那么你没有其他选择,只能找到它的根。