美文网首页Android开发
[崩溃] Android应用永不崩溃的秘诀

[崩溃] Android应用永不崩溃的秘诀

作者: 尹学姐 | 来源:发表于2023-02-13 22:21 被阅读0次

    App的崩溃率,是性能的一个重要的衡量指标。做过客户端开发的朋友,肯定与线上各种各样的崩溃问题打过交道。我们有没有什么办法能提高程序的稳定性、降低崩溃率,甚至做到永不崩溃呢?对于Java崩溃,答案是肯定的。

    根据上一篇文章Java和Android崩溃捕获机制,我们知道了Android崩溃捕获的机制。简单总结就是:所有线程的崩溃,最后都会回调到UncaughtExceptionHandler,调用uncaughtException方法进行处理。

    Android应用程序在发生未捕获崩溃时,会自动退出进程的原因,是因为Android系统在启动的时候,注册了一个KillerAppExceptionHandler的全局 defaultExceptionHandler。所有线程的崩溃都会被这个Handler处理。而这个Handler的处理逻辑非常简单,就是先打印日志,然后退出进程。所以,在Android系统上运行的App,发生未捕获异常的时候,会自动退出进程。

    这篇文章,我们来看看如何实现程序的永不崩溃。

    基本原理

    基本原理其实很简单,我们可以通过设置自己的defaultExceptionHandler,来替代系统默认的KillerAppExceptionHandler。然后在我们的Handler里面,让进程不崩溃,只做一些上报的工作,就可以实现App永不崩溃。

    但是,对于主线程,我们不能直接这样处理。因为主线程是有个Looper,如果程序发生未捕获异常,主线程会被退出,这个时候Looper也就无法继续执行了,会造成ANR,所以主线程需要单独做处理,让Lopper一直不退出。

    下面我们会对子线程和主线程两种情况分别进行讨论。

    子线程

    子线程的情况比较简单,当出现未捕获异常时,直接让线程退出即可。

    sUncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
     // 默认的异常拦截处理器,主线程的异常会用try-catch,不会抛出,所以下面的代码拦截到的都是子线程的异常
    Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
        @Override
        public void uncaughtException(Thread t, Throwable e) {
              if(catchException){
                  // 可以在这里做一些上报
              }else{
                  // 调用原始的uncaughtExceptionHandler进行处理
                  sUncaughtExceptionHandler.uncaughtException();
              }
        }
    });
    

    主线程

    由于主线程需要一直运行,所以不能直接用抛出异常,uncaughtExceptionHandler拦截处理的方式,必须让主线程的循环继续执行下去。

     new Handler(Looper.getMainLooper()).post(new Runnable() {
        @Override
        public void run() {
            while (true) {
                try {
                    //主线程的异常会从这里抛出
                    Looper.loop();
                } catch (Throwable e) {
                    // 可以在这里做一些上报
                }
            }
        }
    });
    

    原理相当于我们在Looper的死循环外面又套了一层死循环,这样当内存的死循环因为抛出异常退出的时候,外层的死循环还能继续执行。

    大致的运行流程:

    • 往主线程的MessageQueue中post一个Runnable,当主线程执行到该Runnable时,会进入我们的while死循环。
    • 在这个循环里面,调用了Looper.loop()方法,使得主线程可以循环起来,读queue中的Message并执行,也就是主线程不会被阻塞。
    • 如果主线程在Message执行中抛出异常,因为我们加了try-catch捕获,所以不会往外抛出异常,而会直接走catch的逻辑。
    • 走完catch之后,又再次进入while循环中。

    可能引起的ANR问题

    如果在主线程抛出异常的时候,拦截异常,但是又不启动主线程的Looper,就会造成ANR。

    比如如下代码,就可能造成ANR的问题:

     Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
            @Override
            public void uncaughtException(final Thread t, Throwable e) {
                return;
            }
     });
    

    所以在使用的UncaughtExceptionHandler的时候,一定要记得重启主线程的Looper,否则即使不崩溃,程序也无法继续运行。

    几个问题

    1. 主线程的方案,相当于在所有的方法执行外层都加了try-catch,会不会影响性能?
      答:性能损耗可以忽略,从反编译出的指令发现,加了try-catch块的代码跟没有加的代码运行时的指令是完全一直的。如果程序运行过程中,不产生异常的话,try-catch几乎不会对运行产生任何影响。只有在发生异常的时候,JVM会追溯宜昌站,这部分耗时就比较高了。

    2. 崩溃后,之后的业务逻辑还能继续执行吗?
      答:不可以,子线程如果遇到崩溃就会直接退出,主线程遇到崩溃,当前的Message的执行也会结束。这个方案只能保证不崩溃,无法保证不会有业务逻辑的问题,所以可以在使用的时候增加一些判断条件,比如,遇到特定的已知的崩溃才进行捕获。

    相关文章

      网友评论

        本文标题:[崩溃] Android应用永不崩溃的秘诀

        本文链接:https://www.haomeiwen.com/subject/hvqjkdtx.html