我开发了一个使用本机 C 库的 Android 应用程序。我可以使用 JNI 成功编译整个东西,一切都很顺利。
但是,本机 C 库时不时会崩溃(最常见的是 SIGSEGV)。反过来,这会导致我的应用程序崩溃,而不会向用户发出任何有意义的通知。我想要实现的目标如下:
如果这对您有任何用处 - 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)在我的调试终端上,我收到以下四条消息
2) 应用程序窗口关闭,但我没有收到 Android 消息 应用程序崩溃时通常会出现“不幸的是...已关闭”
我不太明白为什么我会收到第三条和第四条信号消息,因为我认为抛出了异常。另外,我认为异常永远不会真正抛出(对于 Java)。
我迷路了,非常感谢任何帮助。
我不知道你在这里尝试做的事情在技术上是否可行。 @Michael 的评论暗示这是不可能的。
但如果可能的话,那是个坏主意。
当您使用的本机库触发 SIGSEGV 时,它可能会造成难以估量的损害;例如覆盖堆中的对象等。如果您尝试恢复,已经发生的损坏有可能会导致意外行为或不正确的结果。更糟糕的是,它们可能会导致应用程序锁定或 GC 在一段时间(可能是几个小时)后由于堆损坏、线程卡住等而崩溃。
这就是为什么当本机代码或 Java 代码中出现意外的 SIGSEGV 时,JVM 会出现恐慌是标准行为。以及为什么 JVM 没有提供抑制这些恐慌的方法。
虽然给用户一个有意义的错误很好,但是如果你收到随机的 SIGSEGV 错误和其他 JVM 恐慌,那么告诉他们并没有多大意义。您应该专注于解决恐慌的原因。