在Android上用例外替换JNI崩溃

时间:2017-12-18 17:50:12

标签: java android c android-ndk signals

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

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

  1. 使用信号处理程序(sigaction)捕获本机代码中的信号以防止随机崩溃
  2. 在Java可以捕获的C库中抛出异常
  3. 在Java中捕获异常,为用户生成有意义的警告消息并使应用程序保持运行
  4. 如果这对你有用 - 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 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;
    }
    

    我处理这些信号的功能如下:

    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)。

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

1 个答案:

答案 0 :(得分:0)

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

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

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

这就是为什么在本机代码或Java代码中发生意外的SIGSEGV时,JVM会惊慌的标准行为。

虽然给用户一个有意义的错误是很好的,但是如果告诉您随机的SIGSEGV错误和其他JVM紧急情况,您可以告诉他们的意义不大。