Heim > Fragen und Antworten > Hauptteil
在安卓系统中运行,主要会碰到四种异常,造成应用崩溃退出
runtimeException.
ANR.
自己写的JNI类和C代码产生的信号异常.
第三方so包造成的Native信号异常.
因为我们公司的应用要求绝对不能发生崩溃退出的现象,所以我着手处理这四种异常,前三者都很容易处理(可以通过继承UncaughtExceptionHandler ;或者通过收听系统ANR广播;或者自己写C中模仿java中的try-cache功能捕获信号异常并处理),就第四种比较棘手.
捕获native异常需要做两步工作
在c底层捕获到native信号
收到native信号之后,在c层的信号处理函数,通过反射,调用java中的方法;
在c底层捕获到native信号也很简单,使用signal注册即可,收到native信号之后,就直接走到信号处理函数中了.
最麻烦的是第二步,如何反射回去.
目前的问题是,如何收到native信号之后,在c语言的信号处理函数中,通过反射调用Java中的方法.
普通的c层通过反射调用java层的方法也是没有问题的,但是目前的问题是,在信号处理函数中,无法调用,或者是没有效果,因为当捕获到信号的时候,原来的进程马上就要关闭了.
我目前的试过的方法有
在信号处理函数中,调用原Activity的方法,不好用
在信号处理函数中,调用另一个进程的Activity的方法(在清单文件中配置process),不好用.
在信号处理函数中,抛出异常,抛不出去.
在信号处理函数中,抛出异常(提前取消默认的关闭操作),可以抛出,但是不会被java层的异常捕获类所抓住(但是不是在信号处理函数中抛的,是可以被抓住并处理的).
void debug(const char *format, ... ) {
va_list argptr;
va_start(argptr, format);
__android_log_vprint(ANDROID_LOG_ERROR, "NATIVE_LIB", format, argptr);
va_end(argptr);
}
static JNIEnv *env;
static jobject obj;
static jclass clazz;
static jmethodID methodID;
//------------------------C异常信号的捕获, 复杂的捕获--------------------
const int handledSignals[] = {
SIGSEGV, // 信号11 无效的内存引用
SIGABRT, // 信号 6 来自abort函数的终止信号
SIGFPE, // 信号 8 浮点异常
SIGILL, // 信号 4 非法指令
SIGBUS, // 信号 7 总线错误
SIGALRM // 信号 14 警报器发出的信号
};
//const int handledSignalsNum = sizeof(handledSignals) / sizeof(handledSignals[0]);
enum{handledSignalsNum = sizeof(handledSignals) / sizeof(handledSignals[0])}; // 不用上面而是用这种写法,是为了避免报错Variably modified xxxxxx at file scope
struct sigaction old_handlers[handledSignalsNum];
// 当发生Native崩溃并且发生前面几个信号异常时,就会调用my_sigaction完成信号处理。
void my_sigaction(int signal, siginfo_t *info, void *reserved) {
// Here catch the native crash
debug("收到信号了!");
//// const char*const message = coffeecatch_get_message();
//// debug("** 全局捕获的 FATAL ERROR: %s\n", message);
(*env)->CallVoidMethod(env, obj, methodID, (*env)->NewStringUTF(env, "你好世界"));
}
int loadSigaction() {
struct sigaction handler;
memset(&handler, 0, sizeof(sigaction));
handler.sa_sigaction = my_sigaction;
// 信号处理之后重新设置为默认的处理方式。
// SA_RESTART:使被信号打断的syscall重新发起。
// SA_NOCLDSTOP:使父进程在它的子进程暂停或继续运行时不会收到 SIGCHLD 信号。
// SA_NOCLDWAIT:使父进程在它的子进程退出时不会收到SIGCHLD信号,这时子进程如果退出也不会成为僵 尸进程。
// SA_NODEFER:使对信号的屏蔽无效,即在信号处理函数执行期间仍能发出这个信号。
// SA_RESETHAND:信号处理之后重新设置为默认的处理方式。
// SA_SIGINFO:使用sa_sigaction成员而不是sa_handler作为信号处理函数。
handler.sa_flags = SA_RESETHAND;
int i; // for循环的i需要定义在外面,否则会报错
for (i = 0;i < handledSignalsNum; ++i) {
// if(i == 0){
// // sigaction函数用于改变进程接收到特定信号后的行为。如果把第二、第三个参数都设为NULL,那么该函数可用于检查信号的有效性。
// sigaction(handledSignals[i], // 代表信号编码,可以是除SIGKILL及SIGSTOP外的任何一个特定有效的信号,如果为这两个信号定义自己的处理函数,将导致信号安装错误。
// &handler, // 指向结构体sigaction的一个实例的指针,该实例指定了对特定信号的处理,如果设置为空,进程会执行默认处理。
// SA_RESTART); // 和参数act类似,只不过保存的是原来对相应信号的处理,也可设置为NULL。
// } else {
// // sigaction函数用于改变进程接收到特定信号后的行为。如果把第二、第三个参数都设为NULL,那么该函数可用于检查信号的有效性。
sigaction(handledSignals[i], // 代表信号编码,可以是除SIGKILL及SIGSTOP外的任何一个特定有效的信号,如果为这两个信号定义自己的处理函数,将导致信号安装错误。
&handler, // 指向结构体sigaction的一个实例的指针,该实例指定了对特定信号的处理,如果设置为空,进程会执行默认处理。
&old_handlers[i]); // 和参数act类似,只不过保存的是原来对相应信号的处理,也可设置为NULL。
// }
}
return 1;
}
//简单的捕获的简单的处理
void handler(int sig){
debug("处理");
}
jstring Java_com_wtest_wlib_android_catchs_CatchNativeCrash_loadCatch(
JNIEnv* zenv,
jobject thiz)
{
env = zenv;
obj = thiz;
loadSigaction(); // 加载对JNI异常信号的捕获
// signal(SIGALRM,handler); // 另外一种简答的捕获方式
clazz = (*env)->FindClass(env, "com/wtest/wlib/android/catchs/CatchNativeCrash");
methodID = (*env)->GetMethodID(env, clazz, "show", "(Ljava/lang/String;)V");
(*env)->CallVoidMethod(env, obj, methodID, (*env)->NewStringUTF(env, "你好世界"));
char* cstr = "注册Native信号捕获ok";//jni中几乎都采用这种方式定义c语言的字符串
jstring jstr = (*env)->NewStringUTF(env, cstr);//常用的写法
return jstr;
}