在安卓系统中运行,主要会碰到四种异常,造成应用崩溃退出
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; }