国际惯例先上狗图,以防被打bash
在进行源码分析前我会先普及一些知识做为铺垫,若是了解能够直接略过看正文。markdown
咱们常说的主线程也就是MainThread(也是UiThread,这两个只会在特定状况下不相等),下面是谷歌官网的原话。app
UiThread-->ServiceThread-->HandlerThread-> Thread
复制代码
因此Thread类中的方法,MainThread也有。源码分析
各位看官确定会疑惑为何ThreadGroup能够处理异常并且这个时候不该该返回DefaultUncaughtExceptionHandler来处理吗,请各位看官慢慢看慢慢瞧,不慌,咱们一步步来揭晓post
1.2.1 ThreadGroup从哪里来 其实咱们每一个线程在构造的过程当中都会初始化一个ThreadGroup,只是咱们一般不会手动赋值,而是由系统帮咱们初始化完成。this
public Thread(Runnable target) { init(null, target, "Thread-" + nextThreadNum(), 0); } public Thread(ThreadGroup group, Runnable target) { init(group, target, "Thread-" + nextThreadNum(), 0); } private void init(ThreadGroup g, Runnable target, String name, long stackSize) { Thread parent = currentThread(); if (g == null) { g = parent.getThreadGroup(); } g.addUnstarted(); this.group = g; this.target = target; this.priority = parent.getPriority(); this.daemon = parent.isDaemon(); setName(name); init2(parent); /* Stash the specified stack size in case the VM cares */ this.stackSize = stackSize; tid = nextThreadID(); } 复制代码
经过init方法咱们能够看出在线程初始化当ThreadGroup为null的时候系统会默认拿当前线程的ThreadGroup赋值给建立的子线程。因此在主线程上建立的全部线程在没有单独设置ThreadGroup的状况下他们的ThreadGroup都是同一个,就是主线程的ThreadGroup。spa
1.2.2 如今咱们再来看看为何ThreadGroup能够代替UncaughtExceptionHandler线程
由于ThreadGroup实现了Thread.UncaughtExceptionHandler接口,并默认初始化了两个ThreadGroup(静态),分别是systemThreadGroup和mainThreadGroup(你们看名字就知道他是为主线程服务的),这个两个东西后面有大用你们先记着。 3d
在ThreadGroup的uncaughtException方法中,首先会去寻找该ThreadGroup的 的parent,若是当前ThreadGroup有父ThreadGroup的时,使用父ThreadGroup的uncaughtException方法处理异常。若是没有则会经过Thread获取咱们设置的DefaultUncaughtExceptionHandler(该对象是Thread类中的静态变量)。当咱们没有设置DefaultUncaughtExceptionHandler时该对象默认为null,不过在应用孵化的过程当中系统会对它进行赋值,也就是KillApplicationHandler(它是用来专门杀死进程的后面会讲)。日志
在前面ThreadGroup类内部默认初始化了systemThreadGroup和mainThreadGroup,老司机们一听名字就知道他们是为谁所用。其中mainThreadGroup是以systemThreadGroup为parent,而systemThreadGroup的parent在初始化的时候设置为null。
private ThreadGroup() { // called from C code this.name = "system"; this.maxPriority = Thread.MAX_PRIORITY; this.parent = null; } public ThreadGroup(ThreadGroup parent, String name) { this(checkParentAccess(parent), parent, name); } private ThreadGroup(Void unused, ThreadGroup parent, String name) { this.name = name; this.maxPriority = parent.maxPriority; this.daemon = parent.daemon; this.vmAllowSuspension = parent.vmAllowSuspension; this.parent = parent; parent.add(this); } 复制代码
那么关键来告终合咱们以前1.2.1所说,因为全部主线程建立的子线程默认状况下共用一个ThreadGroup,而这个ThreadGroup就是mainThreadGroup(你们能够去验证主线程的ThreadGroup输出name是不是main).
//在主线程中执行这个方法
Thread.currentThread().getThreadGroup().getName()
复制代码
因此mainThreadGroup在执行uncaughtException时,而且他的parent是systemThreadGroup也就是null,因此全部线程都会调用Thread.getDefaultUncaughtExceptionHandler()来处理异常。咱们一旦初始化了DefaultUncaughtExceptionHandler他就会在当前应用中全局捕获全部线程未处理的异常。
这个时候你们是否是会疑惑由于默认状况下DefaultUncaughtExceptionHandler是为null的,应用是如何处理异常和杀死应用的呢?
由于应用进程由Zygote进程孵化而来,zygote进程fork自身,开启一个Linux进程和一个主线程,ZygoteInit类中的zygoteInit方法随着被调用,该方法中会执行RuntimeInit中的commonInit()方法来设置杀死应用的异常处理器,
应用初始化的过程当中,系统会默认建立KillApplicationHandler设置给DefaultUncaughtExceptionHandler,听名字就知道KillApplicationHandler就是专门用来杀死进程的。其中Thread.setUncaughtExceptionPreHandler(new LoggingHandler())就是咱们不管怎么crash都会打印的日志handler。
下面这个就是ZygoteInit类中的zygoteInit方法,这个方法除了会进行上面所述的调用,还会执行RuntimeInit.applicationInit();在这个方法调用过程当中会经过反射拿到Activity Thread中的main方法,开启主线程的轮询。
public static final Runnable zygoteInit(int targetSdkVersion, String[] argv, ClassLoader classLoader) { if (RuntimeInit.DEBUG) { Slog.d(RuntimeInit.TAG, "RuntimeInit: Starting application from zygote"); } Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ZygoteInit"); RuntimeInit.redirectLogStreams(); RuntimeInit.commonInit(); ZygoteInit.nativeZygoteInit(); return RuntimeInit.applicationInit(targetSdkVersion, argv, classLoader); } 复制代码