Android去除煩人的閃退Dialog

背景
安卓應用閃退後總會出現一個“抱歉,App已經停止運行”的彈窗,這樣的用戶體驗並不好。很多大廠的App都去除了這個彈窗,因此本文主要介紹如何去除默認閃退彈窗,以及在閃退時做一些必要的善後工作。

老規矩,在節目開始之前,先來一個搞笑段子:
又一次坐火車,我坐的是三人一排的那種,我的旁邊是一箇中年大叔,大叔旁邊是一個漂亮的妹子。
我尋思着想找妹子搭搭訕,就對大叔說:“叔叔,我們換個位子行嗎?我和她是朋友。”大叔瞥了我一眼,淡淡地說道:“我是他爸。”

UnCaughtExceptionHandler

UnCaughtExceptionHandler能夠在Thread遇到未catch住的Exception而終止前做一些善後工作。但是它無法阻止線程停止運行,線程最後還是要退出。

Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
    @Override
    public void uncaughtException(Thread thread, Throwable ex) {
        System.exit(1);
    }
});

去除Dialog
Android系統默認設置了一個UnCaughtExceptionHandler,而彈出閃退彈窗的工作就是在這個handler做的。所以如果要去除彈窗,只要實現一個UnCaughtExceptionHandler並替換掉系統默認的就可以了,代碼如下。

public class App extends Application {

    @Override
    public void onCreate() {
        super.onCreate();
        Thread.setDefaultUncaughtExceptionHandler(new MyUnCaughtExceptionHandler());
    }

    class MyUnCaughtExceptionHandler implements Thread.UncaughtExceptionHandler{

        @Override
        public void uncaughtException(Thread thread, Throwable ex) {
            ex.printStackTrace();
            // do some work here
            android.os.Process.killProcess(android.os.Process.myPid());
            System.exit(1);
        }
    }
}

以上的替換UnCaughtExceptionHandler的工作是在Application中統一做的,當然你也可以在每一個Activity中做。對於單個Activity還好,當Activity很多的時候就必需實現一個BaseActivity,在BaseActivity中替換,其它Activity要集成BaseActivity。

必要的善後工作
爲了處理閃退,提升用戶體驗,有必要做一些善後工作,主要有幾條羅列如下:

異常上報
可以採用郵件或者通過服務器接口上傳的方式。兩者各有優缺點,郵件方式開發簡單,但需要用戶額外操作,用戶體驗較差。如果用上傳服務器方式,因爲在UnCaughtExceptionHandler中不能打開一個新的線程,所以只能同步請求,在網絡情況不好的時候花費時間會較長而阻塞運行。也可能因爲網絡原因而上報失敗。當然總體下來還是上傳服務器好一點。具體實現留給讀者。

記錄日誌
將閃退信息存儲到文件系統中。不能存到SharedPreferences中,因爲打開SP需要使用一個新的線程(Android內部實現),而這在UnCaughtExceptionHandler中,這是不被允許的。

重新打開App
可以在UnCaughtExceptionHandler中重新打開App或者彈出自定義彈窗。

class MyUnCaughtExceptionHandler implements Thread.UncaughtExceptionHandler{

    @Override
    public void uncaughtException(Thread thread, Throwable ex) {
        ex.printStackTrace();

        Intent intent = new Intent(App.this, MainActivity.class);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        App.this.startActivity(intent);

        android.os.Process.killProcess(android.os.Process.myPid());
        System.exit(1);
    }
}

注意setFlags這一步是必需的,因爲使用的Context是App的Context,所以必需打開一個新的任務隊列,否則打開Activity無法生效,如果你替換Handler是在Activity做的,拿到的Context是Activity的Context,則無需這一步。

注意事項
最主要的注意點我之前已經提到,不要在UnCaughtExceptionHandler中新開一個線程,會拋出異常。

雖然說正式的商業App都是經過專業測試團隊測試的,不會經常crash,但是誰也不能保證沒有bug,萬一出現crash,這個做法還是可以提升不少用戶體驗的。
http://sixwolf.net/blog/2016/04/11/Android%E5%8E%BB%E9%99%A4%E7%83%A6%E4%BA%BA%E7%9A%84%E9%97%AA%E9%80%80%E5%BC%B9%E7%AA%97/

本期節目就到這裏,感謝大家的收看,下期再見~

發佈了40 篇原創文章 · 獲贊 88 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章