在 Android 上用异常替换 JNI 崩溃[重复]

问题描述 投票:0回答:1

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

但是,本机 C 库时不时会崩溃(最常见的是 SIGSEGV)。反过来,这会导致我的应用程序崩溃,而不会向用户发出任何有意义的通知。我想要实现的目标如下:

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

如果这对您有任何用处 - JNI 代码在单独的线程中运行(更准确地说是在 AsyncTask 内)。

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

遵循在 JNI 代码中抛出异常的最佳方式?如何使用 Java 中基于信号处理的机制将 JNI 崩溃捕获为异常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;
}

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

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");
    }
}

我的关键 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
}

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

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

  • Sighandler:4(对应于SIGILL)
  • 抛出异常
  • 圣戒者:4
  • 信号处理者:11

2) 应用程序窗口关闭,但我没有收到 Android 消息 应用程序崩溃时通常会出现“不幸的是...已关闭”

我不太明白为什么我会收到第三条和第四条信号消息,因为我认为抛出了异常。另外,我认为异常永远不会真正抛出(对于 Java)。

我迷路了,非常感谢任何帮助。

java android c android-ndk signals
1个回答
5
投票

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

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

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

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

虽然给用户一个有意义的错误很好,但是如果你收到随机的 SIGSEGV 错误和其他 JVM 恐慌,那么告诉他们并没有多大意义。您应该专注于解决恐慌的原因。

© www.soinside.com 2019 - 2024. All rights reserved.