App Crash崩潰捕獲

參考 https://github.com/android-notes/Cockroach

原理簡述

  • 利用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的作用僅限於每次主線程拋出異常後迫使主線程再次進入消息循環。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章