國際慣例先上狗圖,以防被打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); } 複製代碼