原理簡述
- 利用
Thread.setDefaultUncaughtExceptionHandler()
設置崩潰捕獲。 - 利用
Handler & Looper
構造新的消息隊列,重啓應用。
Looper
循環
Handler機制就是在一個死循環內部不斷取走阻塞隊列頭部的Message,這個阻塞隊列在主線程中是唯一的,當沒有Message時,循環就阻塞,當一旦有Message時就立馬被主線程取走並執行Message。
查看android源碼可以發現在ActivityThread中main方法(main方法簽名 public static void main(String[] args){}
,這個main方法是靜態的,公有的,可以理解爲應用的入口)最後執行了Looper.loop();
,此方法內部是個死循環(for(;;)循環),所以一般情況下主線程是不會退出的,除非拋出異常。queue.next();
就是從阻塞隊列裏取走頭部的Message,當沒有Message時主線程就會阻塞在這裏,一有Message就會繼續往下執行。android的view繪製,事件分發,activity啓動,activity的生命週期回調等等都是一個個的Message,android會把這些Message插入到主線程中唯一的queue中,所有的消息都排隊等待主線程的執行。
ActivityThread的main方法如下:
public static void main(String[] args) {
...
Looper.prepareMainLooper();//創建主線程唯一的阻塞隊列queue
...
ActivityThread thread = new ActivityThread();
thread.attach(false);//執行初始化,往queue中添加Message等
...
Looper.loop();//開啓死循環,挨個執行Message
throw new RuntimeException("Main thread loop unexpectedly exited");
}
Looper.loop()
關鍵代碼如下:
for (;;) {
Message msg = queue.next(); // might block
...
msg.target.dispatchMessage(msg);//執行Message
...
}
android消息機制僞代碼如下:
public class ActivityThread {
public static void main(String[]args){
Queue queue=new Queue();// 可以理解爲一個加鎖的,可以阻塞線程的ArrayList
queue.add(new Message(){
void run(){
...
print("android 啓動了,下一步該往queue中插入啓動主Activity的Message了");
Message msg=getMessage4LaunchMainActivity();
queue.add(msg);
}
});
for(;;){//開始死循環,for之後的代碼永遠也得不到執行
Message msg=queue.next();
msg.run();
}
}
}
核心代碼
設置崩潰捕獲
Thread.setDefaultUncaughtExceptionHandler(this);
構造新的循環
@Override
public void uncaughtException(final Thread thread, final Throwable throwable) {
// 保存異常信息
mCallback.caughtException(mContext, throwable);
if (thread == Looper.getMainLooper().getThread()) {
while (true) {
try {
Looper.loop();
} catch (Throwable e) {
mCallback.caughtException(mContext, e);
}
}
}
}
核心就在於崩潰捕獲之後構造新的循環,我們在while死循環中又調用了Looper.loop()
,這就導致主線程又開始不斷的讀取queue中的Message並執行,這樣就可以保證以後主線程的所有異常都會從我們手動調用的Looper.loop()
處拋出,一旦拋出就會被try{}catch捕獲,這樣主線程就不會crash了,如果沒有這個while的話那麼主線程下次拋出異常時我們就又捕獲不到了,這樣APP就又crash了,所以我們要通過while讓每次crash發生後都再次進入消息循環,while的作用僅限於每次主線程拋出異常後迫使主線程再次進入消息循環。