素材牛VIP会员
安卓中如何捕获native信号异常,在c底层通过反射调用即将结束的Activity里的方法,并重新打开安卓应用?
 Ca***on  分类:Java代码  人气:730  回帖:1  发布于6年前 收藏

前言

在安卓系统中运行,主要会碰到四种异常,造成应用崩溃退出

  1. runtimeException.

  2. ANR.

  3. 自己写的JNI类和C代码产生的信号异常.

  4. 第三方so包造成的Native信号异常.

因为我们公司的应用要求绝对不能发生崩溃退出的现象,所以我着手处理这四种异常,前三者都很容易处理(可以通过继承UncaughtExceptionHandler ;或者通过收听系统ANR广播;或者自己写C中模仿java中的try-cache功能捕获信号异常并处理),就第四种比较棘手.

捕获native异常需要做两步工作

  1. 在c底层捕获到native信号

  2. 收到native信号之后,在c层的信号处理函数,通过反射,调用java中的方法;

在c底层捕获到native信号也很简单,使用signal注册即可,收到native信号之后,就直接走到信号处理函数中了.
最麻烦的是第二步,如何反射回去.

目前碰到的问题

目前的问题是,如何收到native信号之后,在c语言的信号处理函数中,通过反射调用Java中的方法.

普通的c层通过反射调用java层的方法也是没有问题的,但是目前的问题是,在信号处理函数中,无法调用,或者是没有效果,因为当捕获到信号的时候,原来的进程马上就要关闭了.
我目前的试过的方法有

  1. 在信号处理函数中,调用原Activity的方法,不好用

  2. 在信号处理函数中,调用另一个进程的Activity的方法(在清单文件中配置process),不好用.

  3. 在信号处理函数中,抛出异常,抛不出去.

  4. 在信号处理函数中,抛出异常(提前取消默认的关闭操作),可以抛出,但是不会被java层的异常捕获类所抓住(但是不是在信号处理函数中抛的,是可以被抓住并处理的).

部分c代码

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;
}

讨论这个帖子(1)垃圾回帖将一律封号处理……

Lv6 码匠
Fo***ou 职业无 6年前#1
 文明上网,理性发言!   😉 阿里云幸运券,戳我领取