原理概述
Android系統(tǒng)中的消息機(jī)制是依靠Looper不斷從MessageQueue中取出Message進(jìn)行處理,卡頓的直接原因是處理Message的時(shí)間過(guò)長(zhǎng)张肾,所以監(jiān)控卡頓主要是監(jiān)控Message的處理時(shí)長(zhǎng)饮笛。
BlockCanary通過(guò)重設(shè)Looper中的Printer對(duì)象來(lái)記錄Message的處理時(shí)長(zhǎng),當(dāng)時(shí)長(zhǎng)超過(guò)閥值時(shí)即發(fā)生卡頓,獲取此時(shí)的棧和CPU信息供排查原因棺牧。
使用
//依賴
debugImplementation 'com.github.markzhai:blockcanary-android:1.5.0'
releaseImplementation 'com.github.markzhai:blockcanary-no-op:1.5.0'
//初始化
BlockCanary.install(this, new BlockCanaryContext()).start();
初始化
BlockCanary的參數(shù)設(shè)置在BlockCanaryContext類(lèi)中,通過(guò)繼承可以修改配置
監(jiān)控
初始化拿到BlockCanary單例后便開(kāi)始監(jiān)控
向MainLooper中設(shè)置Printer
Looper在消息處理的過(guò)程中會(huì)通過(guò)Printer打印日志沮翔,BlockCanary正是利用了這一點(diǎn)來(lái)測(cè)量處理Message所耗的時(shí)長(zhǎng)
Printer對(duì)象的初始化在BlockCanaryInternals單例中
Looper通過(guò)調(diào)用Printer的println來(lái)打印日志陨帆,BlockCanary的監(jiān)控必須放在此方法內(nèi)
當(dāng)Message處理的時(shí)間超過(guò)閥值會(huì)回調(diào)onBlockEvent
棧、CPU信息
識(shí)別出了卡頓采蚀,接下來(lái)就是分析卡頓產(chǎn)生的原因
Message在每次處理時(shí)疲牵,都會(huì)記錄棧、CPU信息
棧
StackSampler繼承自AbstractSampler榆鼠,在AbstractSampler內(nèi)部通過(guò)HandlerThread新啟線程纲爸,利用Handler的postDelayed方法循環(huán)取樣
取樣信息存在LinkedHashMap中
CPU
與StackSampler代碼邏輯類(lèi)似,只不過(guò)信息的來(lái)源不同了妆够。CpuSampler的信息是通過(guò)讀取系統(tǒng)log文件的形式獲取的识啦,通過(guò)解析獲取想要的信息組成字符串,存入LinkedHashMap中
信息獲取
發(fā)生卡頓時(shí)神妹,會(huì)回調(diào)onBlockEvent颓哮,四個(gè)參數(shù)分別表示:記錄開(kāi)始時(shí)間點(diǎn)、記錄結(jié)束時(shí)間點(diǎn)鸵荠、記錄開(kāi)始時(shí)間點(diǎn)線程所運(yùn)行時(shí)間冕茅、記錄結(jié)束時(shí)間點(diǎn)線程所運(yùn)行時(shí)間
sStackMap中的key是記錄的時(shí)間點(diǎn),落在記錄開(kāi)始蛹找、結(jié)束時(shí)間點(diǎn)中間的棧信息都是同一Message處理所產(chǎn)生的
同理獲取CPU信息姨伤,組裝成BlockInfo對(duì)象,由LogWriter寫(xiě)入單獨(dú)文件進(jìn)行存儲(chǔ)
信息展示
之前添加的監(jiān)聽(tīng)被回調(diào)
跳轉(zhuǎn)到信息展示頁(yè)面DisplayActivity
在頁(yè)面啟動(dòng)時(shí)加載卡頓信息
加載的過(guò)程是封裝為L(zhǎng)oadBlocks的Runnable
獲取保存的所有日志文件
轉(zhuǎn)成BlockInfoEx對(duì)象
將數(shù)據(jù)設(shè)置到Activity并更新UI