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

Gra*_*eme 51 java-native-interface android exception-handling nullpointerexception android-ndk

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

构建了一组核心功能,并将其包含在我的应用程序中android-ndk.我发现在本机代码中发生的任何异常/崩溃只会在现在和最后再次报告.发生错误时,我会收到以下行为之一:

  • 发生堆栈跟踪/内存转储并将其写入日志文件.该程序消失(设备上没有指示为什么突然该应用程序不再存在).
  • 没有给出堆栈跟踪/转储或其他指示本机代码已崩溃.该程序消失了.
  • java代码崩溃了NullPointerException(通常在每个本机代码异常的相同位置,这是一个巨大的痛苦).通常会让我花费一段时间来尝试调试为什么Java代码抛出错误只是为了发现Java代码很好并且本机代码错误已被完全掩盖.

我似乎无法找到任何方法来"隔离"我的代码以防止在本机代码中发生的错误.Try/catch语句被彻底忽略了.除了我的代码被指责为罪魁祸首之外,我甚至没有机会警告用户而不是发生错误.

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

jav*_*anz 50

我曾经遇到同样的问题,确实在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);
}
Run Code Online (Sandbox Code Playgroud)

请注意,在ThrowNew之后,本机方法不会突然自动终止.也就是说,控制流返回到您的本机方法,此时新的异常处于挂起状态.JNI方法完成后将抛出异常.

我希望这是你正在寻找的解决方案.

  • 虽然这个答案非常适合捕获抛出的C++异常,但它并不处理SIGNAL错误(包括C的NullPointerException版本).这是一篇很棒的文章,如上所述,应该能够严格确定本机应用程序中的错误报告:http://stackoverflow.com/a/1789879/726954 (4认同)
  • 该答案更加详细,可能会更好地满足您的需求:http://stackoverflow.com/a/12014833 (2认同)

oli*_*bre 5

编辑:   另见这个更优雅的答案.


下面的机制基于我在JNI层中成功实现的C预处理器宏.

上面的宏CATCH_CPP_EXCEPTION_AND_THROW_JAVA_EXCEPTION将C++异常转换为Java异常.

替换mypackage::Exception为您自己的C++异常.如果您没有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");          \
  }
Run Code Online (Sandbox Code Playgroud)

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
}
Run Code Online (Sandbox Code Playgroud)

仅供参考或好奇,我在下面提供相应的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
  }
}
Run Code Online (Sandbox Code Playgroud)

首先,example.classexample.java(使用javac或您最喜欢的IDE或maven ...)生成.其次,生成C/C++头文件Java_my_group_mypackage_example.hexample.class使用javah.