You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
android-notes/blogs/Java/UncaughtExceptionHandler.md

3.9 KiB

UncaughtExceptionHandler

目录

  1. 思维导图
  2. 概述
  3. 源码分析
    • Thread#UncaughtExceptionHandler
    • ThreadGroup
    • 总结
  4. 参考

思维导图

概述

在虚拟机中,当一个线程如果没有显式处理(即 try catch)异常而抛出时会将该异常事件报告给该线程对象的 UncaughtExceptionHandler 进行处理,如果线程没有设置 UncaughtExceptionHandler,则默认会把异常栈信息输出到终端而使程序直接崩溃。所以如果我们想在线程意外崩溃时做一些处理就可以通过实现 UncaughtExceptionHandler 来满足需求。

源码分析

Thread#UncaughtExceptionHandler
public class Thread{
        public interface UncaughtExceptionHandler {
        void uncaughtException(Thread t, Throwable e);
    }

    private volatile UncaughtExceptionHandler uncaughtExceptionHandler;

    private static volatile UncaughtExceptionHandler defaultUncaughtExceptionHandler;

	/**
	 * 静态方法,设置一个全局的异常处理器
	 */
    public static void setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler eh) {
         defaultUncaughtExceptionHandler = eh;
     }

    public static UncaughtExceptionHandler getDefaultUncaughtExceptionHandler(){
        return defaultUncaughtExceptionHandler;
    }

    /**
     * 注意,如果 uncaughtExceptionHandler 为空就返回 group
     * 这里的 group 其实是 ThreadGroup
     */
    public UncaughtExceptionHandler getUncaughtExceptionHandler() {
        return uncaughtExceptionHandler != null ?
            uncaughtExceptionHandler : group;
    }

    /**
     * 对指定的线程设置异常处理器
     */
    public void setUncaughtExceptionHandler(UncaughtExceptionHandler eh) {
        checkAccess();
        uncaughtExceptionHandler = eh;
    }
}

看到这就能明白了 setDefaultUncaughtExceptionHandler 和 setUncaughtExceptionHandler 的区别的。线程崩溃时异常抛出的顺序是先调用 Thread 的 getUncaughtExceptionHandler 查看自己是否有自己对象特有的 handler,如果有就直接处理,没有就调用 group(即 ThreadGroup)的逻辑处理。

ThreadGroup
public class ThreadGroup implements Thread.UncaughtExceptionHandler {
    //...
    public void uncaughtException(Thread t, Throwable e) {
        if (parent != null) {
            //通常情况下,parent 为 null
            parent.uncaughtException(t, e);
        } else {
            Thread.UncaughtExceptionHandler ueh =
                Thread.getDefaultUncaughtExceptionHandler();
            if (ueh != null) {
                ueh.uncaughtException(t, e);
            } else if (!(e instanceof ThreadDeath)) {
                System.err.print("Exception in thread \""
                                 + t.getName() + "\" ");
                e.printStackTrace(System.err);
            }
        }
    }
}

这里就清楚了,上面返回的 group 其实就是 ThreadGroup,所以它肯定也是实现 UncaughtExceptionHandler 接口的,它 uncaughtException 的处理逻辑就是先拿全局的 UncaughtExceptionHandler,如果为空就按正常流程抛出异常堆栈信息。

总结
  1. UncaughtExceptionHandler 是一个接口,我们可以通过实现它来处理程序中未被 try-cache 的异常,它有全局异常处理和针对某个线程的异常处理之分
  2. 异常在抛出时,会先检查当前线程是否设置了 UncaughtExceptionHandler,如果有就直接处理,如果没有就跳到 ThreadGroup 中处理,ThreadGroup 也实现了 UncaughtExceptionHandler 接口,它的 uncaughtException 的实现逻辑是判断是否有全局异常处理器,如果有就处理,没有就抛出异常堆栈信息,我们平时的异常信息就是从这里抛出的。

参考

UncaughtExceptionHandler 相关问题解析