本文要點
- 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;^^^^^^^^^^^^^^^^^
在ANRWatchDog的run()中,
首先被利用去判定_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?。。。。。。?!
ANRWatchDog的run()中,
用剛說的主線程Handler,post了_ticker這個任務(wù),
然后自己sleep一段時間【即一個卡頓周期,稍后細說】,
如果sleep結(jié)束之后,如果_tick != 0 && !_reported,
則說明主線程還沒有處理_ticker的run(),
沒有處理_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é)合使用!
參考:


















