Android卡頓優(yōu)化 | ANR分析與實戰(zhàn)(附ANR-WatchDog源碼分析及實戰(zhàn)、與AndroidPerformanceMonitor的區(qū)別)

本文要點

  • ANR概述
  • 發(fā)生ANR后Android系統(tǒng)的執(zhí)行流程
  • ANR-WatchDog原理與實戰(zhàn)
  • ANR的傳統(tǒng)解決套路
  • ANR模擬實戰(zhàn)
  • 線上ANR監(jiān)控方案【ANR-WatchDog原理分析】
  • ANR-WatchDog實戰(zhàn)
  • ANR-WatchDog總結(jié)
  • ANR-WatchDog與AndroidPerformanceMonitor的區(qū)別

項目GitHub

ANR概述

  • KeyDispatchTimeout,5s
    即按鍵或者觸摸事件,在特定的時間(一般5s)之內(nèi)沒有響應(yīng);

  • BroadcastTimeout,前臺10s,后臺60s
    BroadReceiver 在特定的時間(一般前臺10s,后臺60s)之內(nèi)沒有響應(yīng)完成;

  • ServiceTimeout,前臺20s,后臺200s
    Service 在特定的時間(一般前臺20s,后臺200s)之內(nèi)沒有處理完成;

發(fā)生ANR后Android系統(tǒng)的執(zhí)行流程

  • APP發(fā)生ANR
  • 進程接收異常終止信號,開始寫入進程ANR信息(當(dāng)時場景,包含當(dāng)前線程所有堆棧信息、CPU/IO的使用情況等);
  • 彈出ANR提示框,提示用戶關(guān)閉APP或者繼續(xù)等待;(不同ROM表現(xiàn)不同,有的手機廠商會去掉這個提示框)

ANR的傳統(tǒng)解決套路

  • 【線下】在AS的Terminal中,使用
    adb pull data/anr/traces.txt 要存儲在本地的路徑
    導(dǎo)出上面提到的ANR現(xiàn)場信息文件
    導(dǎo)出來后,便可對文件內(nèi)容進行詳細分析:從CPU、IO、鎖沖突等原因思考;

ANR模擬實戰(zhàn)

  • 模擬ANR原因:鎖沖突;

    更改代碼:
    運行程序,等到程序ANR或崩潰,

    在Terminal使用剛剛提到的命令,導(dǎo)出ANR的信息文件:
    生成文件:
    打開文件,可以找到原因:
    上次的BlockCanary同樣捕捉到原因:

線下套路其實就是在APP發(fā)生ANR時,
導(dǎo)出信息文件,
查看文件,結(jié)合代碼進行分析;

線上ANR監(jiān)控方案

  • 通過FileOberver監(jiān)控上述的ANR信息文件的變化,
    如果這個文件發(fā)生了變化,那就說明發(fā)生了ANR,
    那便可以把它上報到服務(wù)器,進行詳細的分析;
    【高版本需注意權(quán)限問題】

  • ANR-WatchDog

    • 依賴compile 'com.github.anrwatchdog:anrwatchdog:1.4.0'
    • 官網(wǎng) https://github.com/SalomonBrys/ANR-WatchDog
    • 原理(源碼分析):ANRWatchDog本身就是Thread的子類:
      ANRWatchDog中,用一個綁定了主線程Looper的Handler,
      去處理_ticker【一個Runnable任務(wù)單元】;
      任務(wù)單元對一些值進行了處理,如_tick、_reported
      _tick在初始為ANRWatchDog的全局變量時,被賦值為0;^^^^^^^^^^^^^^^^^
      ANRWatchDogrun()中,
      首先被利用去判定_ticker被post沒有(因為一開始就_tick為0的話說明_tick還沒被post),
      沒有便將_tick=加上卡頓周期,之后post了_ticker;
      此時_tick不為0!!^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      _ticker中的run(),再一次將_tick置零;^^^^^^^^^^^^^^^^^^^^^^^^^^
      所以只要_ticker不被處理,其run()便不會執(zhí)行,
      _tick就不會被置零,
      由此根據(jù)_tick的值可以判斷_ticker是否被處理了;
      _tick重新歸零則主線程處理了_ticker
      _tick不為零則判定主線程卡頓,它沒處理_tick?。。。。。。?!


      ANRWatchDogrun()中,
      用剛說的主線程Handler,post了_ticker這個任務(wù),
      然后自己sleep一段時間【即一個卡頓周期,稍后細說】,
      如果sleep結(jié)束之后,如果_tick != 0 && !_reported
      則說明主線程還沒有處理_tickerrun(),
      沒有處理_ticker這個任務(wù)單元,
      那便認為主線程發(fā)生了卡頓【如源碼注釋所示】:?。。。。?!
      確定發(fā)生了卡頓,就開始封裝一個ANRError,進行后續(xù)處理了:
      另外補充一下,
      ANRWatchDog提供了兩個重載的構(gòu)造器,
      提供給開發(fā)者對卡頓判定周期進行設(shè)置,開發(fā)者不設(shè)置則使用默認配置
      【跟BlockCanary同一個德行】
      接著仔細看ANRError的構(gòu)造流程
      這里是有兩種構(gòu)造方式New()NewMainOnly(),其最終處理都差不多,
      就是通過mainLooper拿到主線程,
      再通過主線程拿到現(xiàn)場的堆棧信息,
      最后返回構(gòu)造好的ANRError實例:
      拿到ANRError實例之后,
      通過_anrListener.onAppNotResponding(error);回調(diào)機制處理ANRError實例;

      回調(diào)機制就妙??!^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      剛剛的_anrListener.onAppNotResponding(error);只是一個應(yīng)用層上的調(diào)用;
      onAppNotResponding()的實現(xiàn)方式暴露給開發(fā)者了,
      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      在外部可以通過setANRListener()自己定制包含不同處理方式的ANRListener
      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      開發(fā)者不定制,則使用框架自帶的默認處理方式唄:
      處理方式簡單粗暴哈,直接把ANRError丟出去,
      這樣APP就直接崩潰了:
      ANRError乃是Error的子類:

ANR-WatchDog實戰(zhàn)

  • 引入依賴
  • 初始化ANR-WatchDog:
  • 還是上面那個項目,手動阻塞60s,
    運行程序,
    程序會5s后崩潰【5s是默認周期時間,崩潰操作見上面源碼分析】
    logcat定位關(guān)鍵字fatal,可以看到ANRError打印的信息,
    信息中包括了崩潰現(xiàn)場所有線程堆棧信息;
    以及顯示bug代碼的位置;

優(yōu)化:
當(dāng)然默認的APP崩潰處理法并不妥當(dāng),
影響用戶體驗,
實際開發(fā)中,
我們可以自己定義ANRListener,自定義處理方式【上面說過了】,
把堆棧信息上報給服務(wù)器就是了?。。?!

總結(jié)

  • 非浸入式
  • 彌補高版本無權(quán)限問題

與AndroidPerformanceMonitor的區(qū)別

  • AndroidPerformanceMonitor:
    原理是基于Handler-Message機制,
    監(jiān)控主線程每一個Message的執(zhí)行,
    在每一個Message的分發(fā)執(zhí)行前后,進行信息處理;
    (不足:
    一般沒有阻塞的情況下,
    每一個Message的執(zhí)行時間是非常短暫的,
    達不到ANR的級別;
    而且InputEvent在queue.next中block,不會繼續(xù)執(zhí)行dispatchMessage,
    而是從native回調(diào)給InputEventReceiver.dispatchInputEvent處理分發(fā),
    所以BlockCanary也就無法監(jiān)控到這類ANR)
  • ANR-WatchDog:
    不管主線程是怎么執(zhí)行的,
    只管最后的結(jié)果,
    我sleep一個周期之后,就要看我_tick值有沒有被修改,
    沒被修改就是ANR!
  • AndroidPerformanceMonitor適合全程監(jiān)控卡頓,
    ANR-WatchDog適合補充ANR監(jiān)控;
    兩者可以相輔相成,結(jié)合使用!





參考:

?著作權(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)容