如何在Android程序FC前抢救一番

注:此文章中线程不特殊声明,均为Android主线程。

什么是FC,程序为什么会FC

Force Close ,就是程序发生了未被捕捉的异常,也称 FATAL EXCEPTION ,导致程序崩溃,并弹出 令人愉快的 Unfortunately (雾)

程序中出现未被捕捉的异常后发生了什么

  • 抛出异常
  • 交给设置的 UncaughtExceptionHandler 处理
  • 线程结束
  • 程序退出

如何处理

保存或上传崩溃日志

UncaughtExceptionHandler 中写好就可以了。

在崩溃后重启APP

因为在主线程崩溃后,Android的消息机制已经炸了,默认的 UncaughtExceptionHandler 就是并关闭程序弹出 令人愉快的 Unfortunately。可以选择在处理完成后重新启动App。例:

Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
            @Override
            public void uncaughtException(Thread t, Throwable e) {
                //LogUtils.save(e);
                //具体方法网上一大堆,还可以打印机型、系统版本等信息
                Log.e(TAG, "uncaughtException on " + t.toString() + ": ", e);
                //重启App
                context.startActivity(new Intent(context, BootActivity.class).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK));
                System.exit(-1);
            }
        });

防止崩溃

这个问题是我没想到的,因为崩溃都发生了,一般的想法就是安排后事,而不是继续

在此感谢 AndroidDevCN 中的 Aesean

原理解答

在主线程崩溃后,Android的消息机制已经炸了,但是我们有没有办法 一发呢?

了解Android消息机制的都知道(不了解的网上搜一搜吧,这儿 也有一篇blog),Android的主线程就执行了几行代码,大概就是:

public static void main(String[] args) {
    Looper.prepare();
    initMessageQueue();
    Looper.loop();
    throw new RuntimeException("Main thread loop unexpectedly exited");
    //加一句测试代码
    System.out.println("喵喵喵?");
}

UncaughtExceptionHandler 就是在当前线程异常发生时跳转到其中的代码继续执行,然后就退出线程。所以, System.out.println("喵喵喵?"); 并不会执行。 注意,在执行完 UncaughtExceptionHandler 之前线程并没有死。第一段代码中的 t.toString()t 就是主线程。

那么,问题来了,该如何防止崩溃呢?

原理解答

既然线程发生异常,消息队列被破坏,那我们让消息队列继续运行不就可以实现 的目的了?所以,可以这样写:

Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
            @Override
            public void uncaughtException(Thread t, Throwable e) {
                Log.e("TAG", "uncaughtException: ", e);
                Looper.loop();
            }
        });

但是问题又来了,这样只能防止第一次崩溃,第二次依旧会导致程序出现问题。

这就是 Cockroach 这个库所做的。思路很巧妙。

主要源码就是 Cockroach.java

大概分析一下:

Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
            @Override
            public void uncaughtException(Thread t, Throwable e) {
                Log.e("APP", "uncaughtException: ", e);
                while (true) {
                    try {
                        Looper.loop();
                    } catch (Exception e1) {
                        Log.e("APP", "uncaughtException: ", e1);
                    }
                }
            }
        });

就是向 UncaughtExceptionHandler 添加一个死循环,这个死循环又调用 Looper.looper() 来获取 MessageQueue 中的 Message 进行处理,发生异常就处理,然后继续执行 Looper.looper() 。这样消息循环就不会被破坏,线程也不会退出,就可以做到 无限续 的作用。

Cockroach 中还做了一些优化处理,详见源码。

然而有些异常依旧会导致 FC ,比如JNI的异常。所以这个方法也不是万能的,只是一种抢救和优化的方案。

关于 Cockroach ,本身就带了一份 原理说明

对了,今天似乎是虵的生日