【发布时间】:2017-12-18 17:50:12
【问题描述】:
我开发了一个使用原生 C 库的 Android 应用程序。我可以使用JNI成功编译整个东西,一切顺利。
但是,本机 C 库时不时会崩溃(最常见的是 SIGSEGV)。反过来,这会导致我的应用程序在没有任何有意义的通知用户的情况下崩溃。我想要达到的目标如下:
- 使用信号处理程序 (sigaction) 在本机代码中捕获信号以防止随机崩溃
- 在 C 库中引发 Java 可以捕获的异常
- 在 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 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)。
我迷路了,非常感谢任何帮助。
【问题讨论】:
-
在信号处理程序中可以做的事情是相当有限的。例如,在信号处理程序中使用动态内存分配(
ThrowNew听起来它会尝试分配一些内存)在 C++ 中被认为是未定义的行为。我知道这是 C,但那里很可能有类似的限制。
标签: java android c android-ndk signals