在Android上用例外替换JNI崩溃

Mar*_*tin 7 c java android signals android-ndk

我开发了一个使用本机C库的Android应用程序.我可以用JNI成功编译整个东西,一切顺利.

但是,偶尔本机C库崩溃(最常见的是SIGSEGV).反过来,这会导致我的应用程序在没有任何有意义的用户通知的情况下崩溃.我想要实现的目标如下:

  1. 使用信号处理程序(sigaction)捕获本机代码中的信号以防止随机崩溃
  2. 在Java可以捕获的C库中抛出异常
  3. 在Java中捕获异常,为用户生成有意义的警告消息并使应用程序保持运行

如果这对你有用 - JNI代码在一个单独的线程中运行(在AsyncTask中更精确).

我已经检查了http://blog.httrack.com/blog/2013/08/23/catching-posix-signals-on-android/https://github.com/xroche/coffeecatch 但我无法编译.

遵循Best方法在JNI代码中抛出异常的建议如何捕捉JNI崩溃与使用信号处理基础的机制在Java异常https://www.developer.com/java/data/exception-handling-in-jni.html 我进行下面的步骤:

在我的本机代码中,我添加了以下函数(据我所知)设置了一个信号处理程序:

void initializeSignalHandler(JNIEnv* env){
    int watched_signals[] = { SIGABRT, SIGILL, SIGSEGV, SIGINT, SIGKILL }; // 6, 4, 11, 2, 9
    struct sigaction sighandler;
    memset(&sighandler, 0, sizeof(sighandler));

    sighandler.sa_sigaction = &sighandler_func;
    sighandler.sa_mask = 0;
    sighandler.sa_flags = SA_SIGINFO | SA_ONSTACK;
    for(int ii=0; ii<5; ii++){
        int signal = watched_signals[ii];
        sigaction(signal, &sighandler, NULL);        
    }
    env = env;
}
Run Code Online (Sandbox Code Playgroud)

我处理这些信号的函数如下所示:

void sighandler_func(int sig, siginfo_t* sig_info, void* ptr){
    printerr("Sighandler: ", sig);
    jclass jcls = (*env)->FindClass(env, "java/lang/Error");
    jboolean flag = (*env)->ExceptionCheck(env);
    if (flag) {
        (*env)->ExceptionClear(env);
        /* code to handle exception */
    }
    if (jcls!=NULL){
        printerr("Throwing exception");
        (*env)->ThrowNew(env, jcls, "error message");
    }
}
Run Code Online (Sandbox Code Playgroud)

我的关键JNI函数首先配置信号处理程序:

JNIEXPORT jint JNICALL Java_android_playground_criticalFuction
    (JNIEnv *env, jclass c, jlong handle, jshortArray out_buffer){


    // new signal handler
    struct sigaction sighandler;
    initializeSignalHandler(env);

    // ...here goes the critical code
}
Run Code Online (Sandbox Code Playgroud)

当我的本机C代码中出现SIGILL时,会发生以下情况:

1)在我的调试终端上,我收到以下四条消息

  • Sighandler:4(对应于SIGILL)
  • 投掷例外
  • Sighandler:4
  • 西格德勒:11

2)应用程序窗口关闭,但我没有得到Android消息"不幸......已关闭",通常在应用程序崩溃时出现

我真的不明白为什么我得到第三和第四个信号消息,因为我认为抛出了异常.另外,我认为异常永远不会被抛出(对于Java).

我很失落,非常感谢任何帮助.

Ste*_*n C 5

我不知道你在这里尝试做的事情在技术上是否可行。@Michael 的评论暗示这是不可能的。

但如果可能的话,这是一个坏主意。

当您使用的本机代码触发 SIGSEGV 时,它可能会造成难以估量的损害;例如覆盖堆中的对象等。如果您尝试恢复,已经发生的损坏很可能会导致意外行为或不正确的结果。更糟糕的是,它可能会导致应用程序锁定或在一段时间(可能是几个小时)后由于堆损坏、线程卡住等原因导致 GC 崩溃。

这就是为什么当本机代码或 Java 代码中出现意外的 SIGSEGV 时,JVM 会发生恐慌,这是标准行为。以及为什么 JVM 没有提供抑制这些恐慌的方法。

虽然向用户提供有意义的错误固然很好,但如果您遇到随机 SIGSEGV 错误和其他 JVM 恐慌,则告诉他们并没有多大意义。您应该专注于解决恐慌的原因。