作者:Drummor
1.1 認(rèn)識ANR
1.1.1 系統(tǒng)如何處理ANR
設(shè)計原理和影響因素篇,主要對以下關(guān)鍵問題展開
- ANR觸發(fā)的條件以及根本原因
- 發(fā)生ANR之后,系統(tǒng)處理ANR的流程。
- 應(yīng)用層如何判定ANR:對ANR的感知,通過監(jiān)聽SIGQUIT信號。
- 應(yīng)用層面如何獲取有用的信息幫助解決ANR問題。
1.1.2 ANR問題分類
把ANR 產(chǎn)生的影響因素清晰的分為了四個大的類別,基本覆蓋了ANR問題產(chǎn)生的原因。
- 應(yīng)用內(nèi)主線程存在耗時任務(wù);
- 應(yīng)用主線程處理大量任務(wù);
- 系統(tǒng)內(nèi)部其他進(jìn)程或者資源負(fù)載過高;
- 應(yīng)用自身其他線程或者負(fù)載過高。
系列文章其實就是圍繞著這些問題的監(jiān)控、分析、解決展開的。
2 工具建設(shè)-消息調(diào)度監(jiān)控
通過博文得知,今日頭條團(tuán)隊,ANR 監(jiān)控的工具叫 Raster,其最主要的功能就是采集主線程調(diào)度。
ANR 產(chǎn)生的原因很多情況下可能是歷史耗時問題的累計。因而單純采集發(fā)生ANR 時那一刻的堆棧就會有【堆棧漂移的問題】,也就是采集到的堆棧不是誘發(fā)ANR 產(chǎn)生的真正原因?,F(xiàn)在ANR 監(jiān)控的很多框架也是圍繞這一問題展開的。
應(yīng)對方式 對主線程消息調(diào)度進(jìn)行監(jiān)控記錄,包括歷史消息,正在執(zhí)行的消息和將要執(zhí)行的消息。同時對四大組件執(zhí)行的消息進(jìn)行單獨的監(jiān)控,這對我們分析哪個組件產(chǎn)生的ANR 是很重要的參考依據(jù)。
這一方案是在網(wǎng)上公開的我能接觸到的最早提出的,后面我們會看到很多團(tuán)隊對ANR 問題的監(jiān)控都是這一方案的變種,或者大同小異。
這一方案的細(xì)化,主要包括以下幾個方面
2.1 消息聚合
消息統(tǒng)計聚合策略:主線程消息會很多,記錄過去5-10秒的消息本身是一個比較重的動作,采用一定的聚合策略是很有必要的。
- 合并耗時段的多個消息:耗時較小的消息,對ANR 問題的產(chǎn)生影響不大,只記錄總耗時和和消息的個數(shù)。
- 獨立組件消息:
ActivtyThread組件調(diào)度通過Handler我們可以采集到這些調(diào)度,單獨記錄。 - 獨立耗時消息:對超過閾值(比如300ms)的消息單獨記錄,耗時消息是我們重點關(guān)注的對象。
- 記錄IDEL 狀態(tài),主線程無消息的時候,會進(jìn)入IDEL 狀態(tài),堵塞在
nativePoll處。這一狀態(tài)單獨統(tǒng)計。 - 發(fā)生ANR 時,采集當(dāng)前正在進(jìn)行的任務(wù)。
- ANR 發(fā)生時,采集pending消息,根據(jù)pending消息中的組件調(diào)度消息能讓們知道哪個組件觸發(fā) ANR,同時根據(jù)等待的時常側(cè)面反映系統(tǒng)負(fù)載的能力。
2.2 每條消息記錄的關(guān)鍵信息
- 消息調(diào)度的時長:cputime 和 walltime,記錄兩個時間能更好的判斷一次消息耗時是執(zhí)行耗時還是等待或者搶占較多。
- 消息調(diào)度的類型:組件調(diào)度,耗時調(diào)度,多消息聚合調(diào)度
- 消息堆棧:對消息內(nèi)具體執(zhí)行信息,采集其堆棧。

2.3 主線程線程堆棧的采樣
對耗時的消息,進(jìn)行采樣,采取的策略是超時采樣。具體來說,介于大部分的耗時小消息不需要進(jìn)行堆棧采樣,為了避免頻繁設(shè)置和取消超時任務(wù)(也就是采樣任務(wù)),頭條在此處做了一個優(yōu)化,每次消息開始是并不是重新設(shè)置采集超時任務(wù),而是修改目標(biāo)時間。

3 問題分析&解決
- 結(jié)合結(jié)合采集的物料,這些物料包括ANR Info信息,采集的主線程調(diào)度信息等給出了分析ANR問題的一般思路。通過trace信息讀取是否有明顯的耗時調(diào)用,通過ANR Info分析系統(tǒng)負(fù)載,應(yīng)用內(nèi)負(fù)載,再結(jié)合Raster采集的線程調(diào)用,把ANR問題最終歸因上上述四大因素上。然后具體分析解決。這里不做展開了,今日頭條團(tuán)隊案例分析還是相當(dāng)精彩的不容錯過。
- 對Barrier泄漏的監(jiān)控和由SP引發(fā)的案例進(jìn)行詳細(xì)的分析。
2 微信團(tuán)隊
2021年7月份,微信團(tuán)隊發(fā)布在公眾號上的兩篇文章關(guān)于卡頓監(jiān)控和ANR監(jiān)控的文章
- 《微信Android客戶端的卡頓監(jiān)控方案》
- 《微信Android客戶端的ANR監(jiān)控方案》
2.1 首先第一篇《微信Android客戶端的卡頓監(jiān)控方案》
- 該篇是我接觸到的最早也可能是唯一一篇指出使用WatchDog方案漏報卡頓和ANR的問題的文章。文章里甚至還指出了漏報概率公式,以及如何不漏報的方案,如下圖

思考這樣一個問題,如果我們選擇間隔4.5秒去check發(fā)送到主線程Looper到Message是否被消費。現(xiàn)在有一個5秒到卡頓,從2秒開始,結(jié)束在第7秒耗時5秒,我們間隔5秒能監(jiān)控到的概率是多少?
答案是只有11%,驚訝不。原因就是在在0-4.5和4.5到9這兩個周期內(nèi)那些空閑的時間,消息都有可能被消費掉!這種情況我們就監(jiān)控不到了。想想,你再想想,真的是佩服騰訊工程師嚴(yán)謹(jǐn)?shù)墓こ套黠L(fēng)。
- 另外,該篇全面的的提供了監(jiān)控主線程卡頓的方案,包括
- 主線程Message處理的耗時,使用的是設(shè)置Looper Printer的方案,Android10以后我們可以通過設(shè)置Observer監(jiān)控
- IdleHandler耗時的監(jiān)控,通過hook MessageQueue mIdleHandlers實現(xiàn)。
- TouchEvent的耗時監(jiān)控,通過PLT Hook。這一塊要注意,TouchEvent的事件分發(fā)是不通過Handler機制,而是直接native層調(diào)到j(luò)ava層分發(fā)給View的,如果這個知識點不清楚的小伙伴注意了,又有新知識學(xué)習(xí)了。
2.2 第二篇文章
很是精彩,ANR如何產(chǎn)生,系統(tǒng)源碼實現(xiàn),找監(jiān)控方案直接一套組合拳,可見功底深厚大佬就是大佬。
其中,通過監(jiān)聽SIGQUIT信號并過濾誤報最終監(jiān)控ANR的方案,也是當(dāng)前主流的正規(guī)方案,為什么是加一個正規(guī)的修飾詞呢,想想ANRWatchDog那種多不正經(jīng)就知道這個有多正規(guī)。
這里面,幾個重要的點我列下
- 監(jiān)聽SIGQUIT信號,需要注意重新發(fā)送出去
- 梳理系統(tǒng)ANR的過程中,發(fā)現(xiàn)的ANR Trace 通過hook手段可以拿到
這就是微信團(tuán)隊出的關(guān)于Android卡頓和ANR的兩篇經(jīng)典文章,建議大家自信研讀,卷起來!
3 釘釘技術(shù)團(tuán)隊
2022年12月份,釘釘團(tuán)隊發(fā)表的ANR 問題的解決方案系列文章。
- 《釘釘 ANR 治理最佳實踐 | 定位 ANR 不再霧里看花》
- 《讓 nativePollOnce 不再排名第一 | 釘釘 ANR 治理最佳實踐》
- 《釘釘 ANR 實戰(zhàn)踩坑與經(jīng)驗總結(jié) | 釘釘 ANR 治理最佳實踐》
3.1 ANR 判定
作為ANR問題不可避免的兩個問題
- 文章也是分析了系統(tǒng)ANR 的流程
- 判定ANR的方案也是通過監(jiān)聽SIGQUIT 并過濾誤報的方式。
3.2 工具建設(shè)
監(jiān)控ANR 的方案思路與今日頭條基本一致,采集主線程的調(diào)度信息做記錄。其中也有一些不同,把主線程調(diào)度的分類,以下五類

- 主線程的消息調(diào)度
- IdelHandler 調(diào)度
- IDLE 狀態(tài)
- 觸摸事件
- 傳感器事件
可見,其在主線程任務(wù)調(diào)度的方面監(jiān)控的更加具體,今日頭條的Raster工具從博文看只是對主線程的消息調(diào)度進(jìn)行了監(jiān)控,我們在結(jié)合微信團(tuán)隊的卡頓監(jiān)控方案,其實可以更全面的對主線程任務(wù)進(jìn)行調(diào)度,這一點很知道借鑒學(xué)習(xí)。
此外,博文里還提出有些手機廠商在應(yīng)用進(jìn)程會進(jìn)入凍結(jié)狀態(tài),APP 回到前臺后才繼續(xù)執(zhí)行,凍結(jié)的過程里會導(dǎo)致任務(wù)耗時過長,需要單獨記錄,不過這一點今日頭條的方案,里能夠通過CPUTime 和WallTime 識別出來。
其他方面,ANRCanery 采集也會采集過去當(dāng)下和等待的任務(wù)調(diào)度,也對會采集的消息進(jìn)行聚合處理。堆棧采集上,也是采用了時間對齊方案對堆棧進(jìn)行采樣。
3.3 實踐分享
- 結(jié)合具體場景分享了一些死鎖場景,Barrier消息泄露場景等。
- 還分享了工具建設(shè)的心得,很有啟發(fā)性。
4 其他團(tuán)隊
4.1 阿里其他技術(shù)團(tuán)隊
4.1.2 閑魚團(tuán)隊
2021年6月份, 《關(guān)于閑魚的ANR治理,我有幾條心得》
文章短小精悍
- 【監(jiān)控】監(jiān)聽SIGQUIT信號
- 【排查】搭建了ANR Info收集和主線程Message監(jiān)控。
- 【優(yōu)化】給出了三個在借助排查工具的優(yōu)化案例。
3.3 手淘技術(shù)團(tuán)隊
《手淘 Android 幀率采集與監(jiān)控詳解》
手淘團(tuán)隊2022年1月份,提出了Android [滑動幀率]思路并給出了比較詳細(xì)的監(jiān)控方案。
4.2 shopee 團(tuán)隊
2022年8月份 LooperMonitor
《Android 卡頓與 ANR 的分析實踐》
- ANR的判定: 也是通過SIGQUIT +過濾的方式。
- ANR的監(jiān)控 :主線程的監(jiān)控思路與今日頭條和字節(jié)跳動一致,記錄主線程的調(diào)度信息,也是有聚合的策略。
值得注意的點:
- 文章里重點提出了對消息的記錄使用了池化技術(shù),減少內(nèi)存重復(fù)分配問題。
- 采集堆棧的方案上,這里提到了利用Kotlin協(xié)程非堵塞式掛起特性實現(xiàn)了高效的采集。該方案并沒有具體展開如何實現(xiàn)的。同時采集閾值線性增加。
- 對Looper Message的監(jiān)控,在 Android api ≥ 28 時,
Looper中新增了一個Observer的接口,采用元反射的方式,減少了通過Looper Printer 拼接Message帶來的性能損耗。
4.3 毒物團(tuán)隊
2021年9月份得物團(tuán)隊發(fā)表了ANR監(jiān)控文章
《得物技術(shù) | 得物App ANR監(jiān)控平臺設(shè)計》
- ANR判定使用了愛奇藝的XCarash因而能采集到tomsbtone信息
- 主線程消息回溯采集思路與其他家類似。
- 毒物搭建了ANR分析平臺,數(shù)據(jù)可視化,有一定的參考意義。
5 多說一些抓棧問題。
java層面直接通過thread.getStackTrace()獲取堆棧信息,是有一定損耗的,對此我們看到大家常規(guī)的做法是控制采集的頻率,shoppe團(tuán)隊提到的由協(xié)程的非堵塞式掛起實現(xiàn)高效的線程堆棧采集,對此我是持懷疑態(tài)度
業(yè)內(nèi)確實對高效采集堆棧方向有探索,給出了高效的抓棧方案,妥妥的黑科技看到了比較優(yōu)秀的兩篇供大家參考。
- 《西瓜視頻穩(wěn)定性治理體系建設(shè)三:Sliver 原理及實踐》
- 網(wǎng)易云音樂《不一樣的Android堆棧抓取方案》
總結(jié)
縱觀各廠在卡頓和ANR 方面做的探索和方案,我們可以看出,思路上都有重合,在細(xì)節(jié)方面做了很多針對自身業(yè)務(wù)和實際情況做的針對性的優(yōu)化和個性化的開發(fā)??偟膩碚f逃不出以下幾個步驟

- ANR的感知上:業(yè)界主流的方案就是監(jiān)聽SIGQUIT 信號+誤報過濾。騰訊技術(shù)團(tuán)隊,提到的OV 廠商對ANR的處理并不是常規(guī)的處理,而是做閃退處理,所以要以check主線程正在處理的 Message,延誤時間作為輔助防止漏報。
- 信息采集上:由于發(fā)生ANR 時,主線程正在處理的任務(wù)可能并不是發(fā)生ANR的真正原因,因而需要對主線程任務(wù)過往調(diào)度進(jìn)行統(tǒng)計記錄,同時對消息進(jìn)行監(jiān)控,監(jiān)控的類型,其實微信團(tuán)隊卡頓方案監(jiān)控給出了全面的方案。需要對消息進(jìn)行聚合處理。任務(wù)的執(zhí)行棧抓取,控制頻次,也有團(tuán)隊給出了高效抓棧方案。另外,出了主線程調(diào)度消息外,系統(tǒng)負(fù)載情況信息對我們分析ANR 很重要,因為ANR 發(fā)生的原因可不僅僅是主線程執(zhí)行了耗時任務(wù)。
- 問題解決:全面準(zhǔn)確的信息采集的基礎(chǔ)上,會讓找出問題解決問題工作變得更簡單,各團(tuán)隊也給出了思路和案例,參考性很高。
上述ANR 的監(jiān)控方案,截止到現(xiàn)在都沒有開源,卡頓監(jiān)控在Matrix上開源了。talk is cheap ,因而我在正在嘗試取各家之所長,編寫開源一個ANR監(jiān)控工具。目前正在草稿階段,把基本框架做完之后會盡快開源出來,希望大家參與進(jìn)來共同建設(shè)。關(guān)注作者 別錯過更新進(jìn)度。
ANR問題從監(jiān)控到采集都使用了各種hook手段黑科技,ANR作為Android一種保護(hù)機制, 只提出問題,不解決問題的行為都是xxx,目前還未看到官方對此有何動作。