本文要點(diǎn)
- ANR概述
- 發(fā)生ANR后Android系統(tǒng)的執(zhí)行流程
- ANR-WatchDog原理與實(shí)戰(zhàn)
- ANR的傳統(tǒng)解決套路
- ANR模擬實(shí)戰(zhàn)
- 線上ANR監(jiān)控方案【ANR-WatchDog原理分析】
- ANR-WatchDog實(shí)戰(zhàn)
- ANR-WatchDog總結(jié)
- ANR-WatchDog與AndroidPerformanceMonitor的區(qū)別
項(xiàng)目GitHub
ANR概述
KeyDispatchTimeout,5s
即按鍵或者觸摸事件驱证,在特定的時(shí)間(一般5s)之內(nèi)沒有響應(yīng)撑毛;BroadcastTimeout牙躺,前臺(tái)10s鸿吆,后臺(tái)60s
BroadReceiver 在特定的時(shí)間(一般前臺(tái)10s,后臺(tái)60s)之內(nèi)沒有響應(yīng)完成述呐;ServiceTimeout,前臺(tái)20s蕉毯,后臺(tái)200s
Service 在特定的時(shí)間(一般前臺(tái)20s乓搬,后臺(tái)200s)之內(nèi)沒有處理完成;
發(fā)生ANR后Android系統(tǒng)的執(zhí)行流程
- APP發(fā)生ANR
- 進(jìn)程接收異常終止信號(hào)代虾,開始寫入進(jìn)程ANR信息(當(dāng)時(shí)場(chǎng)景进肯,包含當(dāng)前線程所有堆棧信息、CPU/IO的使用情況等)棉磨;
- 彈出ANR提示框江掩,提示用戶關(guān)閉APP或者繼續(xù)等待;(不同ROM表現(xiàn)不同乘瓤,有的手機(jī)廠商會(huì)去掉這個(gè)提示框)
ANR的傳統(tǒng)解決套路
- 【線下】在AS的Terminal中环形,使用
adb pull data/anr/traces.txt 要存儲(chǔ)在本地的路徑
導(dǎo)出上面提到的ANR現(xiàn)場(chǎng)信息文件
;
導(dǎo)出來后衙傀,便可對(duì)文件內(nèi)容進(jìn)行詳細(xì)分析:從CPU抬吟、IO、鎖沖突
等原因思考统抬;
ANR模擬實(shí)戰(zhàn)
-
模擬ANR原因:鎖沖突火本;
更改代碼:
在Terminal使用剛剛提到的命令钙畔,導(dǎo)出ANR的信息文件:
線下套路其實(shí)就是在APP發(fā)生ANR時(shí)金麸,
導(dǎo)出信息文件擎析,
查看文件,結(jié)合代碼進(jìn)行分析钱骂;
線上ANR監(jiān)控方案
通過
FileOberver
監(jiān)控上述的ANR信息文件的變化叔锐,
如果這個(gè)文件發(fā)生了變化,那就說明發(fā)生了ANR见秽,
那便可以把它上報(bào)到服務(wù)器愉烙,進(jìn)行詳細(xì)的分析;
【高版本需注意權(quán)限問題】-
ANR-WatchDog
- 依賴
compile 'com.github.anrwatchdog:anrwatchdog:1.4.0'
- 官網(wǎng) https://github.com/SalomonBrys/ANR-WatchDog
- 原理(源碼分析):
ANRWatchDog
本身就是Thread
的子類:
ANRWatchDog
中解取,用一個(gè)綁定了主線程Looper的Handler步责,
去處理_ticker
【一個(gè)Runnable任務(wù)單元】;
任務(wù)單元對(duì)一些值進(jìn)行了處理,如_tick
蔓肯、_reported
:_tick
在初始為ANRWatchDog
的全局變量時(shí)遂鹊,被賦值為0;^^^^^^^^^^^^^^^^^
在ANRWatchDog
的run()
中蔗包,
首先被利用去判定_ticker
被post沒有(因?yàn)橐婚_始就_tick
為0的話說明_tick
還沒被post)秉扑,
沒有便將_tick
=加上卡頓周期
,之后post了_ticker
调限;
此時(shí)_tick
不為0V勐健!^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ticker
中的run()
耻矮,再一次將_tick
置零秦躯;^^^^^^^^^^^^^^^^^^^^^^^^^^
所以只要_ticker
不被處理,其run()
便不會(huì)執(zhí)行裆装,
_tick
就不會(huì)被置零踱承,
_tick
的值可以判斷_ticker
是否被處理了;
_tick
重新歸零則主線程處理了_ticker
哨免,
_tick
不為零則判定主線程卡頓
茎活,它沒處理_tick
!W镣佟C钌!;鬯!I肀妗!芍碧!
ANRWatchDog
的run()
中煌珊,
用剛說的主線程Handler,post了_ticker
這個(gè)任務(wù)泌豆,
然后自己sleep一段時(shí)間【即一個(gè)卡頓周期定庵,稍后細(xì)說】,
如果sleep結(jié)束之后踪危,如果_tick != 0 && !_reported
蔬浙,
則說明主線程還沒有處理_ticker
的run()
,
沒有處理_ticker
這個(gè)任務(wù)單元,
那便認(rèn)為主線程
發(fā)生了卡頓
【如源碼注釋所示】:U暝丁3氩!@吨佟>悴 官疲!
確定發(fā)生了卡頓,就開始封裝一個(gè)ANRError
亮隙,進(jìn)行后續(xù)處理了:
ANRWatchDog
提供了兩個(gè)重載的構(gòu)造器,
提供給開發(fā)者對(duì)卡頓判定周期
進(jìn)行設(shè)置溢吻,開發(fā)者不設(shè)置則使用默認(rèn)配置
:
【跟BlockCanary
同一個(gè)德行】ANRError
的構(gòu)造流程New()
维费、NewMainOnly()
,其最終處理都差不多促王,
就是通過mainLooper
拿到主線程掩完,
再通過主線程拿到現(xiàn)場(chǎng)的堆棧信息
,
最后返回構(gòu)造好的ANRError
實(shí)例:ANRError
實(shí)例之后硼砰,
通過_anrListener.onAppNotResponding(error);
回調(diào)機(jī)制處理ANRError
實(shí)例;
回調(diào)機(jī)制就妙靶琅稹题翰!^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
剛剛的_anrListener.onAppNotResponding(error);
只是一個(gè)應(yīng)用層上的調(diào)用;
onAppNotResponding()
的實(shí)現(xiàn)方式暴露給開發(fā)者了诈胜,
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
在外部可以通過setANRListener()
自己定制包含不同處理方式的ANRListener
:
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
處理方式簡(jiǎn)單粗暴哈,直接把ANRError
丟出去焦匈,
這樣APP就直接崩潰
了:ANRError
乃是Error
的子類:
- 依賴
ANR-WatchDog實(shí)戰(zhàn)
- 引入依賴
-
初始化ANR-WatchDog:
-
還是上面那個(gè)項(xiàng)目血公,手動(dòng)阻塞60s,
運(yùn)行程序缓熟,
程序會(huì)5s后崩潰【5s是默認(rèn)周期時(shí)間累魔,崩潰操作見上面源碼分析】
在logcat
定位關(guān)鍵字fatal
,可以看到ANRError
打印的信息够滑,
信息中包括了崩潰現(xiàn)場(chǎng)所有線程
的堆棧信息
垦写;
以及顯示bug代碼的位置
;
優(yōu)化:
當(dāng)然默認(rèn)的APP崩潰處理法并不妥當(dāng)彰触,
影響用戶體驗(yàn)梯投,
實(shí)際開發(fā)中,
我們可以自己定義ANRListener
况毅,自定義處理方式【上面說過了】分蓖,
把堆棧信息上報(bào)給服務(wù)器就是了!6怼C春住!
總結(jié)
- 非浸入式
- 彌補(bǔ)高版本無權(quán)限問題
與AndroidPerformanceMonitor的區(qū)別
- AndroidPerformanceMonitor:
原理是基于Handler-Message機(jī)制味廊,
監(jiān)控主線程每一個(gè)Message的執(zhí)行午磁,
在每一個(gè)Message的分發(fā)執(zhí)行前后尝抖,進(jìn)行信息處理;
(不足:
一般沒有阻塞的情況下迅皇,
每一個(gè)Message的執(zhí)行時(shí)間是非常短暫的昧辽,
達(dá)不到ANR的級(jí)別;
而且InputEvent在queue.next中block登颓,不會(huì)繼續(xù)執(zhí)行dispatchMessage搅荞,
而是從native回調(diào)給InputEventReceiver.dispatchInputEvent處理分發(fā),
所以BlockCanary也就無法監(jiān)控到這類ANR) - ANR-WatchDog:
不管主線程是怎么執(zhí)行的框咙,
只管最后的結(jié)果咕痛,
我sleep一個(gè)周期之后,就要看我_tick
值有沒有被修改喇嘱,
沒被修改就是ANR茉贡! AndroidPerformanceMonitor
適合全程監(jiān)控卡頓,
ANR-WatchDog
適合補(bǔ)充ANR監(jiān)控者铜;
兩者可以相輔相成腔丧,結(jié)合使用!
參考: