1.前言
ANR比較棘手在于,沒(méi)有崩潰日志,定位問(wèn)題比較困難,而且ANR是必須要解決的問(wèn)題。
Android對(duì)ANR的監(jiān)控機(jī)制
Android應(yīng)用程序是通過(guò)消息來(lái)驅(qū)動(dòng)的,Android某種意義上也可以說(shuō)成是一個(gè)以消息驅(qū)動(dòng)的系統(tǒng),UI、事件、生命周期都和消息處理機(jī)制息息相關(guān)。Android的ANR監(jiān)測(cè)方案也是一樣,大部分就是利用了Android的消息機(jī)制。

2.主流的ANR監(jiān)控方案
主流的ANR監(jiān)控方案:FileObserver,WatchDog,Looper.loop。它們都各有優(yōu)缺點(diǎn),無(wú)法覆蓋所有情況,所以在線上使用的時(shí)候可以結(jié)合這幾種方案一起使用。
2.1 FileObserver實(shí)現(xiàn)ANR監(jiān)控
當(dāng)ANR發(fā)生的時(shí)候,我們是可以通過(guò)監(jiān)聽(tīng)該文件目錄data/anr/的寫(xiě)入情況來(lái)判斷是否發(fā)生了ANR,看起來(lái)這是一個(gè)不錯(cuò)的時(shí)機(jī)。需要注意的是,所有應(yīng)用發(fā)生ANR的時(shí)候都會(huì)進(jìn)行回調(diào),因此需要做一些過(guò)濾與判斷,如包名、進(jìn)程號(hào)等。
優(yōu)點(diǎn):
- 基于原生接口調(diào)用,時(shí)機(jī)和內(nèi)容準(zhǔn)確
- 無(wú)性能問(wèn)題實(shí)現(xiàn)簡(jiǎn)單
缺點(diǎn):
最大的困難是兼容性問(wèn)題,這個(gè)方案受限于Android系統(tǒng)的SELinux機(jī)制,5.0以后基本已經(jīng)使低權(quán)限應(yīng)用無(wú)法監(jiān)聽(tīng)到trace文件了,但是可以在開(kāi)發(fā)內(nèi)測(cè)階段通過(guò)root手機(jī)進(jìn)行監(jiān)控。
2.2 ANR-WatchDog實(shí)現(xiàn)ANR監(jiān)控
ANR-WatchDog是參考Android WatchDog機(jī)制(com.android.server.WatchDog.java)起個(gè)單獨(dú)線程向主線程發(fā)送一個(gè)變量+1操作的消息,然后自我休眠sleep 自定義ANR的閾值一般是5s,休眠過(guò)后判斷變量是否+1完成,如果未完成則告警。

優(yōu)點(diǎn):
- 兼容性好,各個(gè)機(jī)型版本通用
- 無(wú)需修改APP邏輯代碼,非侵入式
- 邏輯簡(jiǎn)單,性能影響不大
缺點(diǎn):
無(wú)法保證能捕捉所有ANR,對(duì)閾值的設(shè)置直接影響捕獲概率.比如如果設(shè)置了5s就無(wú)法捕獲10s的Receiver和20s的service 引起的ANR。
2.3 Looper.loop實(shí)現(xiàn)ANR監(jiān)控
巧妙的利用了Android原生Looper.loop中的一個(gè)log打印邏輯。
這個(gè)log打印邏輯正是在Message消息分發(fā)前后,大部分的性能卡頓問(wèn)題都是在這里發(fā)生的,監(jiān)控這兩個(gè)邏輯之間的時(shí)間差就可以得到當(dāng)前主線程的卡頓狀態(tài),如果超時(shí)則獲取trace信息并上報(bào)。
實(shí)現(xiàn)原理:開(kāi)啟子線程執(zhí)行,會(huì)消耗cpu資源,謹(jǐn)慎開(kāi)啟,開(kāi)發(fā)中一般針對(duì)部分用戶下發(fā)開(kāi)關(guān):
- 設(shè)置Looper.setPrinter(自定義printer)實(shí)現(xiàn)println方法;
- 在消息執(zhí)行前后,Looper會(huì)調(diào)用
Looper.mPriter.pritlin(“>>>>> dispatch msg.target msg.callBack,msg.what”)//子線程開(kāi)啟收集線程堆棧信息
Looper.mPriter.pritlin(“<<<<<< finish msg.target msg.callBack,msg.what”)//子線程結(jié)束收集線程堆棧信息- 首先需要判斷msg.target的Looper是否等于主線程的Looper.
- 其次計(jì)算這兩個(gè)函數(shù)前后時(shí)間差是否超過(guò)200ms超過(guò)的話就上報(bào)堆棧調(diào)用信息。
優(yōu)點(diǎn):
靈活配置可監(jiān)控常見(jiàn)APP應(yīng)用性能也可作為一部分場(chǎng)景的ANR監(jiān)測(cè),并且可以準(zhǔn)確定位ANR和耗時(shí)調(diào)用棧。
谷歌已經(jīng)明確標(biāo)注This must be in a local variable, in case a UI event sets the logger這個(gè)looger對(duì)象是可以被更改的,已經(jīng)有開(kāi)發(fā)者遇到在使用WebView時(shí)logger被set為Null導(dǎo)致,進(jìn)而造成監(jiān)控失敗。
3.如果dispatchMessage消息執(zhí)行的非常久是無(wú)法觸發(fā)監(jiān)控的邏輯。
4.無(wú)法監(jiān)控CPU資源緊張?jiān)斐上到y(tǒng)卡頓,無(wú)法響應(yīng)的ANR
2.4 注冊(cè)信號(hào)函數(shù)
使用注冊(cè)信號(hào)函數(shù)機(jī)制監(jiān)聽(tīng)SINGAL_QUIT信號(hào),可以監(jiān)聽(tīng)ANR和Crash觸發(fā)邏輯。可以作為FileObserver在5.0以后因權(quán)限問(wèn)題無(wú)法監(jiān)測(cè)data/anr目錄的使用。
因?yàn)楫?dāng)應(yīng)用發(fā)生crash和ANR的時(shí)候,系統(tǒng)會(huì)向目標(biāo)進(jìn)程發(fā)送SIGNAL_QUIT信號(hào),應(yīng)用虛擬機(jī)捕獲到信號(hào)以后會(huì)收集系統(tǒng)信息輸出到日志文件中(data/anr/trace.txt)然后kill掉目標(biāo)進(jìn)程。trace文件中數(shù)據(jù)過(guò)大,可以根據(jù)當(dāng)前時(shí)間節(jié)點(diǎn)和進(jìn)程id進(jìn)行信息過(guò)濾。詳情參考:讓Native Crash 與ANR無(wú)處發(fā)泄
3.總結(jié)
ANR監(jiān)控方案各有優(yōu)劣,開(kāi)發(fā)中一般組合多個(gè)共同使用。
Crash,ANR,卡頓最難的是異常信息的收集上傳,收集到信息后可以根據(jù)日志進(jìn)行分析進(jìn)而解決問(wèn)題。信息(一般是將日志保存在本地,有效期限是七天)上傳一般都是通過(guò)開(kāi)關(guān)動(dòng)態(tài)下發(fā)的,目的是為了節(jié)省帶寬和服務(wù)端的存儲(chǔ)壓力。千萬(wàn)級(jí)的用戶數(shù)據(jù)量會(huì)很大。
在灰度期間重點(diǎn)觀察指標(biāo)是否正常,如果正常擴(kuò)大灰度繼續(xù)觀察,不正常的話就停止灰度。然后定位問(wèn)題是升級(jí)還是采用熱修復(fù)。