線下場景我們能直接連接設(shè)備debug,所以監(jiān)控緯度很多,我們從4個方面入手(就像是中醫(yī)的望聞聽切)來介紹相關(guān)監(jiān)控工具的使用。(如果你已經(jīng)很熟悉的話,可以直接跳過)
1.UI
2.CPU
3.內(nèi)存
4.整體
在下面的章節(jié)會逐一介紹,本節(jié)先介紹UI。
本節(jié)我們主要介紹可以用來分析UI的工具 systrace和Perfetto。
這兩個工具都是Android系統(tǒng)提供的系統(tǒng)追蹤工具,可追蹤/監(jiān)控應(yīng)用各個方面的數(shù)據(jù),包括CPU、內(nèi)存、view、IO等,其中Perfetto是Android 10(API 29)推出的,用來取代systrace。但鑒于目前Android10的份額有限,我們這兩個都介紹下。
一.systrace
具體使用有兩種方式,第一種圖形化方式,Android Device Monitor(SDK tools中的monitor.bat):

點擊紅框處按鈕彈出對話框,對想監(jiān)控的指標(biāo)進(jìn)行勾選,然后生成trace.html,再用Chrome瀏覽器打開查看數(shù)據(jù)。

但這種方式經(jīng)常有問題,打開后空白,也沒有錯誤提示。所以不推薦,我們使用第二種方式。
第二種方式,使用python命令。有兩個前提工作:
(1) 首先安裝python2.7版本(必須為這一版本)
(2) 配置環(huán)境變量(這里不再展開說明)
其次,應(yīng)用還必須是可debug的,設(shè)備可能還需要root。
環(huán)境準(zhǔn)備好后, 在cmd中進(jìn)入你的android sdk \platform-tools\systrace目錄,接著輸入命令即可。
命令的格式是這樣的:
systrace.py [options] [categories]
(關(guān)于options和categories所有屬性以及注意事項后面?zhèn)渥?
先直接舉個簡單的例子:
systrace.py -t 5 -o systrace.html view
意思是記錄整個設(shè)備的view繪制信息,持續(xù)5S,輸出到systrace.html文件中。

用Chrome打開這個html(其它瀏覽器也可打開,但顯示不夠完整)。
打開后,是這樣的,因為是捕獲整個系統(tǒng)的,所以有很多進(jìn)程,需要手動選擇自己的那個進(jìn)程。

選定了自己的進(jìn)程后,信息還是很多,有種無從下手的感覺
不用擔(dān)心,右側(cè)面板的alerts會給我們一些提示(不一定是問題,但可以是優(yōu)化方向)。

可以看到,有三種提示類型,分別是過渡測量/布局、長繪制、過大bigmap。我們選中第一個Expensive measure/layout,可以看到左側(cè)有幾個圓圈標(biāo)紅,其余的都變成了黑灰色。每個圓圈里面有個F,這是frame的縮寫,代表每一幀的繪制。顏色越深繪制時間越長,正常的要在16ms以內(nèi)。

我們可以使用鍵盤的ws ad鍵進(jìn)行放大、縮小,和左移右移,M鍵查看(聚焦)。
我們挑選第一個F,再按m鍵查看,可以看到這一幀足足花費了324ms。

更近一步的查看,我們只需要關(guān)注UI Thread和Render Thread這兩個即可。
UI Thread顯然就是主線程了,它負(fù)責(zé)處理進(jìn)程 Message、處理 Input 事件、處理 Animation 邏輯、處理 Measure、Layout、Draw ,更新 DisplayList ,但是不涉及 SurfaceFlinger;Render Thread稱之為渲染線程,負(fù)責(zé)渲染渲染相關(guān)的工作(基于硬件加速)。
在Android 5.0之前,這些操作都是在主線程進(jìn)行的,為了減輕主線程的負(fù)擔(dān),之后進(jìn)行了獨立。
Android系統(tǒng)的UI從繪制到顯示到屏幕是分兩步進(jìn)行的:第一步是在Android應(yīng)用程序進(jìn)程這一側(cè)進(jìn)行的;第二步是在SurfaceFlinger進(jìn)程這一側(cè)進(jìn)行的。第一步將UI繪制到一個圖形緩沖區(qū)中,然后交給第二步來進(jìn)行合成及顯示。
按W鍵放大視圖,可以看到UI Thread中有熟悉的layout 方法(序號1),我們單擊它,下方(序號2)處給出了alert的提示:layout花費大量時間,雖然wall duration(持續(xù)時間)只有1.494ms,,, 然而當(dāng)我們想弄清楚到底是哪個view或者是具體的代碼導(dǎo)致的,這里的信息就不夠了。

不過systrace提供了代碼標(biāo)記的方法,在我們覺得有可能會出現(xiàn)卡頓的地方打上標(biāo)記,這樣在視圖里就可以展現(xiàn)。具體如下:
//代碼開始
Trace.beginSection("tag");
//代碼結(jié)束
Trace.endSection();
開始和結(jié)束必須在同一線程中。
為了支持這個標(biāo)記功能,在py命令里還要加上“-a 包名”
為了方便查找,我在tag后加了三個*號,可以直接在序號1處填入你的tag進(jìn)行搜索,并且可以跳轉(zhuǎn)(需要配合縮放移動),在序號2處可以看到我們添加標(biāo)記的那段代碼的執(zhí)行時間,這樣就有一個直觀的認(rèn)識了。

二.Perfetto
Perfetto 是 Android 10 中引入的全新平臺級跟蹤工具,用來替換systrace。不僅適用于 Android、還適用Linux 和 Chrome的。
它的入口就是個網(wǎng)頁:https://ui.perfetto.dev (網(wǎng)絡(luò)有時候很差)
基本上可分為三種使用方式:
1.直接debug
2.設(shè)備上生成 .perfetto-trace文件導(dǎo)入查看
3.通過adb命令生成文件導(dǎo)入查看
1.直接debug,設(shè)備得是android 10及以上的:

我們需要把GPU里的兩個開關(guān)打開。然后點擊recording開始記錄即可,默認(rèn)10S。然后自動生成結(jié)果頁面。

2.在設(shè)備上記錄,”開發(fā)者選項”-”系統(tǒng)跟蹤”,不過有的Android 10手機上并沒有此選項,比如華為手機。

類別里我們選擇和UI相關(guān)的即可

然后選擇快捷設(shè)置圖標(biāo)(會在通知欄上顯示狀態(tài))

最后開始記錄


記錄完畢后,用adb命令導(dǎo)出即可:
adb pull /data/local/traces/.
然后如下圖所示,在Perfetto平臺導(dǎo)入即可。

2.adb 命令生成
先生成文件在設(shè)備上
adb shell perfetto --config :test --out /data/misc/perfetto-traces/trace

然后再從設(shè)備上pull下來
adb pull /data/misc/perfetto-traces/trace

這里只是最簡單的命令,詳細(xì)可參考;
https://developer.android.google.cn/studio/command-line/perfetto
導(dǎo)入后的結(jié)果頁面長這樣,比較友好,使用方式和原理基本與systrace一樣,我們就不做過多的介紹了。

想更多了解下還是看看官網(wǎng)吧:
https://perfetto.dev/
三.代碼
可以用Window().addOnFrameMetricsAvailableListener來打印出每幀的耗時情況,僅支持android 7.0以上的設(shè)備
boolean isListener = true;
private void onFrameListener() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
getWindow().addOnFrameMetricsAvailableListener(new Window.OnFrameMetricsAvailableListener() {
@Override
public void onFrameMetricsAvailable(Window window, FrameMetrics frameMetrics, int i) {
Log.d("onFrameListener", "measure + layout=" + frameMetrics.getMetric(FrameMetrics.LAYOUT_MEASURE_DURATION) / 1000000 + "ms" +
" delay=" + frameMetrics.getMetric(FrameMetrics.UNKNOWN_DELAY_DURATION) / 1000000 + "ms" +
" anim=" + frameMetrics.getMetric(FrameMetrics.ANIMATION_DURATION) / 1000000 + "ms" +
" touch=" + frameMetrics.getMetric(FrameMetrics.INPUT_HANDLING_DURATION) / 1000000 + "ms" +
" draw=" + frameMetrics.getMetric(FrameMetrics.DRAW_DURATION) / 1000000 + "ms" +
" total=" + frameMetrics.getMetric(FrameMetrics.TOTAL_DURATION) / 1000000 + "ms");
if (!isListener) {
getWindow().removeOnFrameMetricsAvailableListener(this);
}
}
}, new Handler());
}
}
使用方式就是給window加個監(jiān)聽,當(dāng)不需要的時候,移除監(jiān)聽即可。
打印結(jié)果如下:

當(dāng)你在操作頁面的時候,可以實時打印出結(jié)果,還是挺方便的。這樣就將頁面的流暢度通過數(shù)字量化了,不再是個主觀上的”感覺”。
除了這一種方法外,還有其它的方法,可參考:
http://m.itdecent.cn/p/c647d090e9c8
UI總結(jié):
本節(jié)主要介紹了監(jiān)控UI的一些工具。
systrace使用門檻較高,需要有一定的底層知識才能分析,并且提供的信息不夠精確,不好定位且打標(biāo)簽有代碼侵入性。
perfetto對版本的要求比較高,大部分的低版本設(shè)備都無法使用。
一般需要結(jié)合其它工具來一起使用,比如我們已經(jīng)很熟悉的 過度繪制、GPU呈現(xiàn)模式分析、Hierarchy Viewer,Layout Inspector等等。很多大廠如字節(jié)跳動的內(nèi)部監(jiān)控工具也是基于systrace做的。我們這里僅僅只能提供入門講解。
除了系統(tǒng)提供的工具,adb命令也可以監(jiān)控到UI的一些信息,比如dumpsys,它可以獲取所有系統(tǒng)服務(wù)的診斷輸出。例如:"adb dumpsys gfxinfo 包名" 來查看幀的耗時數(shù)據(jù)。不過頁面不是很友好,上手成本比較高。
其實只要能定位到問題所在其實就好辦,很多時候就是難在不好定位。以上這些工具就是幫我們定位的。定到位后再去開個藥方針對性治療,關(guān)于怎么治,這里也不再展開說了,無外乎就是那些 不要在draw方法里耗時操作,提前渲染好需要展示的資源(錯峰/延時加載)、layout不要嵌套太多、循環(huán)中不要新建過多對象等等。
AS自帶的Layout Inspector布局檢查器,我們就不過多介紹了。
官方介紹:https://developer.android.google.cn/studio/debug/layout-inspector#layout-inspector
另外還有很多開源工具,大家可以了解下:

最后,強烈推薦查看谷歌”親爸爸”的關(guān)于UI性能優(yōu)化的文章:
https://developer.android.google.cn/training/testing/performance
備注:
1.systrace命令的補充說明:
systrace.py [options] [categories]
[options]:
-a <package_name>:這個選項可以開啟指定包名App中自定義Trace Label的Trace功能。也就是說,如果你在代碼中使用了Trace.beginSection("tag"), Trace.endSection;默認(rèn)情況下,你的這些代碼是不會生效的,因此,這個選項一定要開啟!
-t N:用來指定Trace運行的時間
-l:這個用來列出你分析的那個手機系統(tǒng)支持的Trace模塊;
-o FILE:指定trace數(shù)據(jù)文件的輸出路徑,如果不指定就是當(dāng)前目錄的trace.html。
[category]:
常用的有這幾項:
sched: CPU調(diào)度的信息,非常重要;你能看到CPU在每個時間段在運行什么線程;線程調(diào)度情況,比如鎖信息。
gfx:Graphic系統(tǒng)的相關(guān)信息,包括SerfaceFlinger,VSYNC消息,Texture,RenderThread等;分析卡頓非常依賴這個。
view: View繪制系統(tǒng)的相關(guān)信息,比如onMeasure,onLayout等;對分析卡頓比較有幫助。
am:ActivityManager調(diào)用的相關(guān)信息;用來分析Activity的啟動過程比較有效。
dalvik: 虛擬機相關(guān)信息,比如GC停頓等。
binder_driver: Binder驅(qū)動的相關(guān)信息,如果你懷疑是Binder IPC的問題,不妨打開這個。
core_services: SystemServer中系統(tǒng)核心Service的相關(guān)信息,分析特定問題用
2.線程狀態(tài)查看
如下圖所示位置會顯示線程的執(zhí)行情況:
綠色:Running 線程正在完成與進(jìn)程相關(guān)的工作或正在響應(yīng)中斷。
藍(lán)色:Runnable 線程可以運行但當(dāng)前沒有安排。
白色:Sleeping 線程沒有工作要做,可能是因為線程在互斥鎖上被阻塞。
橙色:Uninterruptable sleep(不間斷的睡眠) 線程在I/O上被阻塞或等待磁盤操作完成。
紫色:Interruptable sleep(可以中斷睡眠) 線程在另一個內(nèi)核操作(通常是內(nèi)存管理)上被阻塞。
3.相關(guān)時間解釋
Wall Duration 持續(xù)時間
CPU Duration CPU耗時
Self Time 自身方法耗時(不包含其調(diào)用方法
CPU Self Time 自身方法CPU執(zhí)行時間
擴(kuò)展:
1.Systrace中典型的一幀繪制流程

這張圖對應(yīng)的工作流程如下:
1.主線程處于 Sleep 狀態(tài),等待 Vsync 信號
2.Vsync 信號到來,主線程被喚醒,Choreographer 回調(diào) FrameDisplayEventReceiver.onVsync 開始一幀的繪制
3.處理 App 這一幀的 Input 事件(如果有的話)
4.處理 App 這一幀的 Animation 事件(如果有的話)
5.處理 App 這一幀的 Traversal 事件(如果有的話)
6.主線程與渲染線程同步渲染數(shù)據(jù),同步結(jié)束后,主線程結(jié)束一幀的繪制,可以繼續(xù)處理下一個 Message(如果有的話,IdleHandler 如果不為空,這時候也會觸發(fā)處理),或者進(jìn)入 Sleep 狀態(tài)等待下一個 Vsync
7.渲染線程首先需要從 BufferQueue 里面取一個 Buffer(dequeueBuffer) , 進(jìn)行數(shù)據(jù)處理之后,調(diào)用 OpenGL 相關(guān)的函數(shù),真正地進(jìn)行渲染操作,然后將這個渲染好的 Buffer 還給 BufferQueue (queueBuffer) , SurfaceFlinger 在 Vsync-SF 到了之后,將所有準(zhǔn)備好的 Buffer 取出進(jìn)行合成。
2.systrace快捷鍵使用
快捷鍵的使用可以加快查看 Systrace 的速度,下面是一些常用的快捷鍵
W : 放大 Systrace,放大可以更好地看清局部細(xì)節(jié)
S : 縮小 Systrace,縮小以查看整體
A : 左移
D : 右移
M : 高亮選中當(dāng)前鼠標(biāo)點擊的段(這個比較常用,可以快速標(biāo)識出這個方法的左右邊界和執(zhí)行時間,方便上下查看)
鼠標(biāo)模式快捷切換 : 主要是針對鼠標(biāo)的工作模式進(jìn)行切換,默認(rèn)是 1,也就是選擇模式,查看 Systrace 的時候,需要經(jīng)常在各個模式之間切換,所以點擊切換模式效率比較低,直接用快捷鍵切換效率要高很多
數(shù)字鍵1 : 切換到 Selection 模式,這個模式下鼠標(biāo)可以點擊某一個段查看其詳細(xì)信息,一般打開 Systrace 默認(rèn)就是這個模式,也是最常用的一個模式,配合 M 和 ASDW 可以做基本的操作
數(shù)字鍵2 : 切換到 Pan 模式,這個模式下長按鼠標(biāo)可以左右拖動, 有時候會用到
數(shù)字鍵3 : 切換到 Zoom 模式,這個模式下長按鼠標(biāo)可以放大和縮小,有時候會用到
數(shù)字鍵4 : 切換到 Timing 模式,這個模式下主要是用來衡量時間的,比如選擇一個起點,選擇一個終點,查看起點和終點這中間的操作所花費的時間。
參考:
systrace常識:
https://blog.csdn.net/f2006116/article/details/107581327