【问题标题】:Replacing JNI Crashes by Exceptions on Android [duplicate]在Android上用异常替换JNI崩溃[重复]
【发布时间】:2017-12-18 17:50:12
【问题描述】:

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

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

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

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

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

遵循Best way to throw exceptions in JNI code?How to catch JNI Crashes as exceptions using Signal handling based mechanism in Javahttps://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)
  • 抛出异常
  • Sighandler:4
  • Sighandler:11

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

我真的不明白为什么我会收到第三条和第四条信号消息,因为我认为抛出了异常。此外,我认为异常从来没有真正被抛出(Java)。

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

【问题讨论】:

  • 在信号处理程序中可以做的事情是相当有限的。例如,在信号处理程序中使用动态内存分配(ThrowNew 听起来它会尝试分配一些内存)在 C++ 中被认为是未定义的行为。我知道这是 C,但那里很可能有类似的限制。

标签: java android c android-ndk signals


【解决方案1】:

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

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

当您使用的本机库触发 SIGSEGV 时,它很可能已经造成无法估量的损害;例如覆盖堆中的对象等。如果您尝试恢复,之前的损坏可能会导致意外行为或不正确的结果……或者稍后由于堆损坏导致 GC 崩溃。

这就是为什么当本机代码或 Java 代码中出现意外的 SIGSEGV 时,JVM 会出现恐慌是标准行为。

虽然给用户一个有意义的错误很好,但你可以告诉他们没有多大意义......如果你得到随机的 SIGSEGV 错误和其他 JVM 恐慌。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-02-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多