Android崩潰日志收集是個什么鬼?

目前大多數(shù)app都是使用三方庫(例如友盟)實現(xiàn)崩潰日志收集, 但不一定了解是如何實現(xiàn)的。 今天工作不忙, 剛好有時間思考一下這個問題。

我們知道Android進程在閃退或崩潰時, logcat里會輸出一片紅色的崩潰日志, 包括Shutting down vm和堆棧信息。 PS: Android基礎(chǔ)知識點:app和linux進程是什么關(guān)系? 答:每個android進程可以理解為是一個linux進程。

下面說說我理解的做日志采集的套路:
1、 自定義Application類, 一般在這個類里初始化三方庫。
<pre>
<application
android:name=".TheApplication"
android:icon="${icon}"
android:label="${str}"
android:largeHeap="true"
tools:replace="android:label"></pre>

2、 保存默認異常處理Handler的引用(Java虛虛擬機的異常日志都會執(zhí)行回調(diào)函數(shù)uncaughtException), 注意handler是運行在主線程里! (PS: 為什么不是子線程? 我的理解是子線程默認沒有l(wèi)ooper, 而創(chuàng)建android進程時AndroidNative.java里會創(chuàng)建一個looper。)
mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();

3、 將uncaughtException函數(shù)的參數(shù)轉(zhuǎn)換為字符串, 即異常日志堆棧信息。


異常堆棧.png

4、 為了更好的分析問題,除了異常堆棧外,可能還需要app版本號、用戶信息(例如用戶名或手機號)、手機信息(如機型、版本等)、當前進程的線程信息等等, 將這些信息保存到數(shù)據(jù)庫表里(可以用原生的sqlite或三方庫如Realm、LitePal、OrmLite、GreenDao等等), 寫到數(shù)據(jù)庫的目的在于能夠多條批量上傳,更重要的是避免進程崩潰后日志丟失或者不知道是否已上傳的狀態(tài)。 PS:當然還可以在捕獲到異常時保存其它你關(guān)心的數(shù)據(jù)!

5、 上傳日志可以單獨啟動個遠程服務(wù)(運行在:remote進程,目的是避免占用UI進程資源), 使用觀察者模式監(jiān)聽數(shù)據(jù)庫變化或監(jiān)聽當前時間和上次上傳日志的時間間隔, 當日志記錄條數(shù)超出閾值或者超出間隔周期時, 將日志打包成gzip或其它壓縮格式并傳送到服務(wù)器, 服務(wù)器存儲結(jié)果并返回成功時, 客戶端刪除對應(yīng)的日志記錄。
PS: HTTP交互是在子線程執(zhí)行的, 可以借助三方庫例如OkHttp實現(xiàn)。

思路示例代碼(省略了存數(shù)據(jù)和打包上傳的代碼):
<pre>
public class TheApplication extends Application {
Thread.UncaughtExceptionHandler mDefaultHandler;

@Override
public void onCreate() {
    super.onCreate();
    mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();  //第一步,獲取默認handler

    //替換handler, 這是在主線程里執(zhí)行
    Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
        @Override
        public void uncaughtException(Thread t, Throwable e) {
            Writer writer = new StringWriter();
            PrintWriter printWriter = new PrintWriter(writer);
            e.printStackTrace(printWriter);
            Throwable cause = e.getCause();
            while (cause != null) {
                cause.printStackTrace(printWriter);
                cause = cause.getCause();
            }
            printWriter.close();
            String result = writer.toString();   //這就是異常日志堆棧信息

            /**
              第三步, 存儲app版本號、用戶信息(例如uid或手機號)、手機信息(例如機型、版本號)、
             當前進程的線程信息和result(即異常堆棧到一個數(shù)據(jù)庫表里(狀態(tài)位表示未上傳,已上傳時刪除該記錄)
            */

            /**
             * 第四步, 上傳異常日志的服務(wù)器。 進程里應(yīng)該有個服務(wù),監(jiān)聽著數(shù)據(jù)庫異常日志表(觀察者模式)或啟動服務(wù)
             * 時判斷記錄條數(shù)是否達到閾值; 超過閾值時, 將多條記錄打包壓縮成gzip或其它格式上傳到服務(wù)器, 服務(wù)器
             * 存儲數(shù)據(jù)后返回成功, 客戶端刪除本地日志表的對應(yīng)記錄。
             */

            mDefaultHandler.uncaughtException(t, e);
            // Java的默認異常處理。 如果是NullPointerException, 注釋掉這行app會無響應(yīng),因為"AndroidRuntime: Shutting down VM"
        }
    });</pre>
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容