UncaughtExceptionHandler异常捕获及最佳实践

在Android系统内为什么会出现Crash?

简单来说,就是程序存在没有被try...catch的异常代码块,导致应用进程被强制销毁,系统Thread类内部有一个静态变量,它能监测到进程当前进程内,某个线程出现异常而没有被捕获的情况,然后对其进行善后处理:

private static volatile UncaughtExceptionHandler defaultUncaughtExceptionHandler;

下面来说说系统是怎么善后的?系统有一个RuntimeInit类 ,可以理解成所有进程的父类,这样每个进程初始化的时候,都是走其main()方法,如下:
在RuntimeInit.java类

public static final void main(String[] argv) {
         ......
        commonInit();
         ...... 
    }

里面主要有一个方法 commonInit()

protected static final void commonInit() {
        if (DEBUG) Slog.d(TAG, "Entered RuntimeInit!");

        /*
         * set handlers; these apply to all threads in the VM. Apps can replace
         * the default handler, but not the pre handler.
         */
        LoggingHandler loggingHandler = new LoggingHandler();
        Thread.setUncaughtExceptionPreHandler(loggingHandler);
     Thread.setDefaultUncaughtExceptionHandler(newKillApplicationHandler(loggingHandler));
        
        ......
    }

可以看到,方法里面主要是配置了两个Thread.UncaughtExceptionHandler对象,一个是LoggingHandler:这个是给Android Framework层使用,应用层无需关心,另外一个是KillApplicationHandler:这个是单独给应用层去善后事宜的,我们需要重点关注此类:

/**
     * Handle application death from an uncaught exception.  The framework
     * catches these for the main threads, so this should only matter for
     * threads created by applications. Before this method runs, the given
     * instance of {@link LoggingHandler} should already have logged details
     * (and if not it is run first).
     */
    private static class KillApplicationHandler implements Thread.UncaughtExceptionHandler {
        private final LoggingHandler mLoggingHandler;

        public KillApplicationHandler(LoggingHandler loggingHandler) {
            this.mLoggingHandler = Objects.requireNonNull(loggingHandler);
        }

        @Override
        public void uncaughtException(Thread t, Throwable e) {
            try { 
                // Don't re-enter -- avoid infinite loops if crash-reporting crashes.
                if (mCrashing) return;
                   mCrashing = true;

                if (ActivityThread.currentActivityThread() != null) {
                    ActivityThread.currentActivityThread().stopProfiling();
                }
                // step1
                // Bring up crash dialog, wait for it to be dismissed
                ActivityManager.getService().handleApplicationCrash(
                        mApplicationObject, new ApplicationErrorReport.ParcelableCrashInfo(e));
            } catch (Throwable t2) {
                if (t2 instanceof DeadObjectException) {
                    // System process is dead; ignore
                } else {
                    try {
                        // step2
                        Clog_e(TAG, "Error reporting crash", t2);
                    } catch (Throwable t3) {
                        // Even Clog_e() fails!  Oh well.
                    }
                }
            } finally {
                // step 3
                // Try everything to make sure this process goes away.
                Process.killProcess(Process.myPid());
                System.exit(10);
            }
        }
    }

有没有很熟悉,第一步,弹出crash的对话框;第二步:crash日志输出;第三步,在finally 里面杀掉进程退出,我们在开发过程中是不是也经常遇到这样的情况?好了 我们回到我们主题上来,我们应用可以怎么巧妙的使用UncaughtExceptionHandler呢?

UncaughtExceptionHandler 使用技巧

1. 自定义UncaughtExceptionHandler

有时候,我们APP有这样的应用场景,比如某个线程出现未被成功捕获的异常,我们需要自定义全局捕获的UncaughtExceptionHandler,并且给一个交互更好的提示框或者文案,这时,需要我们自定义全局UncaughtExceptionHandler,那我们如何自定义呢?
在我们application主进程初始化时,在Thread中set自定义的UncaughtExceptionHandler对象:

Thread.setDefaultUncaughtExceptionHandler(new MyUncaughtExceptionHandler());

设置完成之后,就可以替代系统的UncaughtExceptionHandler了,我们可以在自定义的对象里面做自己像做的事情:

  private class MyUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler {
      @Override
      public void uncaughtException(Thread t, Throwable e) {
          // do something
          // 退出程序
          android.os.Process.killProcess(android.os.Process.myPid());
          System.exit(0);
      }
  }

2. 同时使用多个UncaughtExceptionHandler

第一点,只是告诉我们自定义UncaughtExceptionHandler,但是有时候,我们不需要覆盖掉系统的KillApplicationHandler,那怎么办呢?
其实可以这样:

private class MyUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler {

        private Thread.UncaughtExceptionHandler mUncaughHandler;

        public MyUncaughtExceptionHandler(){
            mUncaughHandler = Thread.getDefaultUncaughtExceptionHandler();
        }

        @Override
        public void uncaughtException(Thread t, Throwable e) {

            // do something
            // 退出程序
            android.os.Process.killProcess(android.os.Process.myPid());
            System.exit(0);
            
            // 系统的 UncaughtExceptionHandler
            mUncaughHandler.uncaughtException(t,e);
        }
    }

先把系统UncaughtExceptionHandler存下来,放到最后在调用。

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容