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

时间:2011-12-07 12:05:20

标签: android exception-handling java-native-interface android-ndk nullpointerexception

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

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

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

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

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

3 个答案:

答案 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.hexample.class生成C / C ++头文件javah

答案 2 :(得分:0)

您是否考虑过捕获此异常然后将其包装在运行时异常中,只是为了让它在堆栈中更高?

我在SCJD中使用了类似的'hack'。一般来说,NPE表示您的错误,但如果您确信自己没有做错任何事情,那么只需记录好RuntimeException即可解释异常用于冒泡异常。然后打开它并测试是否为NPE的例子并将其作为您自己的例外处理。

如果它会导致错误的数据,那么你没有其他选择,只能找到它的根。