捕獲程式Crash,讓你的APP告別閃退

sydMobile發表於2017-12-25

文章最早釋出於我的微信公眾號 Android_De_Home 中,歡迎大家掃描下面二維碼關注微信公眾獲取更多知識內容。
可以隨意轉載,但請務必註明出處!

為什麼出現這種方法

當APP線上程中跑出了異常就會導致APP crash。比如我們最常見的NullPointerException空指標異常。有些時候我們不希望這種異常導致我們的APP crash,尤其是在debug狀態下,程式很大的時候,編譯執行一次也不容易,debug的時候好不容易程式啟動起來了,發生了crash就不能debug執行了,有時候會很耽誤開發。所有有了這個自定義的異常處理。它可以捕獲你的異常,使程式不會crash,這樣就可以繼續除錯了。

具體操作介紹

首先介紹Thread.UncaughtExceptionHandler

UncaughtExceptionHandler文件

文件說明:當一個執行緒突然的因為沒有捕獲到的異常而停止的時候會由這個介面來處理。當一個執行緒由於沒有捕獲的異常而終止的時候,Java虛擬機器將查詢這個執行緒通過UncaughtExceptionHandler的getUncaughtExceptionHandler。並將呼叫uncaughtExcetion方法,並且把這個執行緒和異常作為引數傳遞過去。如果一個執行緒沒有他的UncaughtExceptionHandler設定, 那麼它的ThreadGroup物件作為它的UncaughtExceptionHandler。如果ThreadGroup物件沒有特別的處理異常要求,它可以呼叫getDefaultUncaughtExceptionHandler。即系統預設的的 UncaughtExceptionHandler。

簡單來說UncaughtExceptionHandler就是用於線上程中當一些系統沒有捕獲的異常發生的時候來處理這些異常的。你可以使用系統預設的處理方式,你也可以通過Thread.setDefaultUncaughtExceptionHandler()方法設定你自己定義的異常處理。

uncaughtException_method

注意Thread.setDefaultUncaughtExceptionHandler(CustomUncaughtExceptionHandler)後,只能保證當在你的程式中如果crash沒有發生在UI執行緒(主執行緒)中而是在別的執行緒中的時候,這個時候APP是不會出現崩潰的現象的。如果在主執行緒中出現crash後,APP還是會崩潰的。

進一步防止程式出現Crash

開頭已經說了,有很多時候雖然我們的APP會因為各種問題閃退,但是在更多的時候我們是不希望,我的APP閃退的這就出現了下面的方法。

首先說明這種方法在Activity初始化的時候可能會導致你的APP出現類似ANR的情況(其實並不是ANR,只是狀態看起來像,造成的原因是因為Activity還沒有完成初始化,也就是生命週期還沒有執行完畢就遇到異常了,導致了頁面沒法顯示,所以在正式釋出的APP中還是要慎重使用)

如何使用呢?需要和你的後臺商量好,在程式中做好標誌控制該不該使用ExceptionHandler來處理。如果你的程式某個地方出現大量crash的時候,而這個功能是在Activity初始化後(可能是由於點選某個按鈕觸動的問題)這個時候你就可以用ExceptionHandler來處理了,讓使用者在點選這個按鈕後,不至於程式崩潰掉。

核心程式碼:

	 new Handler(Looper.getMainLooper()).post(new Runnable() {
            @Override
            public void run() {

                while (true) {
                    try {
                        Looper.loop();
                    } catch (Throwable throwable) {
                       
                            
								               
                    }
                }
            }
        });
複製程式碼

通過Handler向主執行緒的queue中新增一個Runnabel(此處new Handler()裡面傳引數Looer.getMainLooper()就是為了是向主執行緒的queue中新增,如果不傳這個引數就是預設的了)當主執行緒執行到我們傳送的這個Runnable的時候就會進入我們的while死迴圈,如果while內部是空的話就會造成程式碼卡死在這裡導致ANR,但是我們在while中呼叫了Looper.loop(),這就使得我們的主執行緒又開始工作了,不斷的從queue中獲取Message執行(其實Android的機制就是這樣的不斷的從主執行緒的queue中獲取message並且執行)。這樣就可以保證以後主執行緒的所有異常都會從我們手動呼叫的Looper.loop()處丟擲了。

下面分析一下為什麼這樣就可以保證主執行緒的所有異常都會從我們手動呼叫的Looper.loop()處丟擲。

ActivityThread_main()

首先看主執行緒的原始碼,我的主執行緒都是在main()函式裡面開始執行的,就像Java一樣,這裡是入口。我們可以看到在main函式中呼叫了Looper.loop()。

Looper.loop()

Looper.loop()方法的原始碼,在這裡就很明顯了,我們可以看到在這個方法中有一個for(;;)死迴圈,不斷的從queue中獲取message執行。所以我們的方法就變成了

	  for(;;){
            Message msg = queue.next();
            //如果msg是我們post過來的Runnable就會執行下面的程式碼了
            while(true){
                try{
                    Looper.loop();(的本質就是 for(;;){.....})
                }catch(....){
                    ........

                }

            }

        }
複製程式碼

所以當出現異常的時候就會丟擲了。所以這樣做的話就可以保證所有主執行緒中的異常可以被捕獲了,單純這樣做的話,子執行緒中如果出現問題的話還是會crash的,所以就要加上Thread.setDefaultUncaughtExceptionHandler()了。

原理基本就介紹完了,結合寫的程式碼(程式碼裡面有詳細的使用註釋)看會更清晰一些。程式碼地址和使用方法點選 這裡

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


關注公眾號,及時獲取推送
關注公眾號,及時獲取推送

相關文章