線下場(chǎng)景-UI(systrace + perfetto)

線下場(chǎng)景我們能直接連接設(shè)備debug,所以監(jiān)控緯度很多,我們從4個(gè)方面入手(就像是中醫(yī)的望聞聽(tīng)切)來(lái)介紹相關(guān)監(jiān)控工具的使用。(如果你已經(jīng)很熟悉的話,可以直接跳過(guò))
1.UI
2.CPU
3.內(nèi)存
4.整體
在下面的章節(jié)會(huì)逐一介紹,本節(jié)先介紹UI。
本節(jié)我們主要介紹可以用來(lái)分析UI的工具 systrace和Perfetto。

這兩個(gè)工具都是Android系統(tǒng)提供的系統(tǒng)追蹤工具,可追蹤/監(jiān)控應(yīng)用各個(gè)方面的數(shù)據(jù),包括CPU、內(nèi)存、view、IO等,其中Perfetto是Android 10(API 29)推出的,用來(lái)取代systrace。但鑒于目前Android10的份額有限,我們這兩個(gè)都介紹下。

一.systrace

具體使用有兩種方式,第一種圖形化方式,Android Device Monitor(SDK tools中的monitor.bat):

圖片1.png

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

圖片2.png

但這種方式經(jīng)常有問(wèn)題,打開(kāi)后空白,也沒(méi)有錯(cuò)誤提示。所以不推薦,我們使用第二種方式。

第二種方式,使用python命令。有兩個(gè)前提工作:
(1) 首先安裝python2.7版本(必須為這一版本)
(2) 配置環(huán)境變量(這里不再展開(kāi)說(shuō)明)

其次,應(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所有屬性以及注意事項(xiàng)后面?zhèn)渥?huì)寫(xiě))

先直接舉個(gè)簡(jiǎn)單的例子:
systrace.py -t 5 -o systrace.html view

意思是記錄整個(gè)設(shè)備的view繪制信息,持續(xù)5S,輸出到systrace.html文件中。

圖片3.png

用Chrome打開(kāi)這個(gè)html(其它瀏覽器也可打開(kāi),但顯示不夠完整)。
打開(kāi)后,是這樣的,因?yàn)槭遣东@整個(gè)系統(tǒng)的,所以有很多進(jìn)程,需要手動(dòng)選擇自己的那個(gè)進(jìn)程。

圖片4.png

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

圖片5.png

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

圖片6.png

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

圖片7.png

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

圖片8.png

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

圖片9.png

二.Perfetto

Perfetto 是 Android 10 中引入的全新平臺(tái)級(jí)跟蹤工具,用來(lái)替換systrace。不僅適用于 Android、還適用Linux 和 Chrome的。

它的入口就是個(gè)網(wǎng)頁(yè):https://ui.perfetto.dev (網(wǎng)絡(luò)有時(shí)候很差)

基本上可分為三種使用方式:
1.直接debug
2.設(shè)備上生成 .perfetto-trace文件導(dǎo)入查看
3.通過(guò)adb命令生成文件導(dǎo)入查看

1.直接debug,設(shè)備得是android 10及以上的:

圖片10.png

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

圖片11.png

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


圖片12.png

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

圖片13.png

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

圖片14.png

最后開(kāi)始記錄

圖片15.png
圖片16.png

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

圖片17.png

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

圖片18.png

然后再?gòu)脑O(shè)備上pull下來(lái)
adb pull /data/misc/perfetto-traces/trace

圖片19.png

這里只是最簡(jiǎn)單的命令,詳細(xì)可參考;
https://developer.android.google.cn/studio/command-line/perfetto

導(dǎo)入后的結(jié)果頁(yè)面長(zhǎng)這樣,比較友好,使用方式和原理基本與systrace一樣,我們就不做過(guò)多的介紹了。


圖片20.png

想更多了解下還是看看官網(wǎng)吧:
https://perfetto.dev/

三.代碼

可以用Window().addOnFrameMetricsAvailableListener來(lái)打印出每幀的耗時(shí)情況,僅支持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加個(gè)監(jiān)聽(tīng),當(dāng)不需要的時(shí)候,移除監(jiān)聽(tīng)即可。
打印結(jié)果如下:

圖片20.5.png

當(dāng)你在操作頁(yè)面的時(shí)候,可以實(shí)時(shí)打印出結(jié)果,還是挺方便的。這樣就將頁(yè)面的流暢度通過(guò)數(shù)字量化了,不再是個(gè)主觀上的”感覺(jué)”。

除了這一種方法外,還有其它的方法,可參考:
http://www.itdecent.cn/p/c647d090e9c8

UI總結(jié):

本節(jié)主要介紹了監(jiān)控UI的一些工具。
systrace使用門(mén)檻較高,需要有一定的底層知識(shí)才能分析,并且提供的信息不夠精確,不好定位且打標(biāo)簽有代碼侵入性。
perfetto對(duì)版本的要求比較高,大部分的低版本設(shè)備都無(wú)法使用。
一般需要結(jié)合其它工具來(lái)一起使用,比如我們已經(jīng)很熟悉的 過(guò)度繪制、GPU呈現(xiàn)模式分析、Hierarchy Viewer,Layout Inspector等等。很多大廠如字節(jié)跳動(dòng)的內(nèi)部監(jiān)控工具也是基于systrace做的。我們這里僅僅只能提供入門(mén)講解。
除了系統(tǒng)提供的工具,adb命令也可以監(jiān)控到UI的一些信息,比如dumpsys,它可以獲取所有系統(tǒng)服務(wù)的診斷輸出。例如:"adb dumpsys gfxinfo 包名" 來(lái)查看幀的耗時(shí)數(shù)據(jù)。不過(guò)頁(yè)面不是很友好,上手成本比較高。
其實(shí)只要能定位到問(wèn)題所在其實(shí)就好辦,很多時(shí)候就是難在不好定位。以上這些工具就是幫我們定位的。定到位后再去開(kāi)個(gè)藥方針對(duì)性治療,關(guān)于怎么治,這里也不再展開(kāi)說(shuō)了,無(wú)外乎就是那些 不要在draw方法里耗時(shí)操作,提前渲染好需要展示的資源(錯(cuò)峰/延時(shí)加載)、layout不要嵌套太多、循環(huán)中不要新建過(guò)多對(duì)象等等。

AS自帶的Layout Inspector布局檢查器,我們就不過(guò)多介紹了。
官方介紹:https://developer.android.google.cn/studio/debug/layout-inspector#layout-inspector
另外還有很多開(kāi)源工具,大家可以了解下:

圖片21.png

最后,強(qiáng)烈推薦查看谷歌”親爸爸”的關(guān)于UI性能優(yōu)化的文章:
https://developer.android.google.cn/training/testing/performance

備注:

1.systrace命令的補(bǔ)充說(shuō)明:
systrace.py [options] [categories]
[options]:
-a <package_name>:這個(gè)選項(xiàng)可以開(kāi)啟指定包名App中自定義Trace Label的Trace功能。也就是說(shuō),如果你在代碼中使用了Trace.beginSection("tag"), Trace.endSection;默認(rèn)情況下,你的這些代碼是不會(huì)生效的,因此,這個(gè)選項(xiàng)一定要開(kāi)啟!
-t N:用來(lái)指定Trace運(yùn)行的時(shí)間
-l:這個(gè)用來(lái)列出你分析的那個(gè)手機(jī)系統(tǒng)支持的Trace模塊;
-o FILE:指定trace數(shù)據(jù)文件的輸出路徑,如果不指定就是當(dāng)前目錄的trace.html。
[category]:
常用的有這幾項(xiàng):
sched: CPU調(diào)度的信息,非常重要;你能看到CPU在每個(gè)時(shí)間段在運(yùn)行什么線程;線程調(diào)度情況,比如鎖信息。
gfx:Graphic系統(tǒng)的相關(guān)信息,包括SerfaceFlinger,VSYNC消息,Texture,RenderThread等;分析卡頓非常依賴(lài)這個(gè)。
view: View繪制系統(tǒng)的相關(guān)信息,比如onMeasure,onLayout等;對(duì)分析卡頓比較有幫助。
am:ActivityManager調(diào)用的相關(guān)信息;用來(lái)分析Activity的啟動(dòng)過(guò)程比較有效。
dalvik: 虛擬機(jī)相關(guān)信息,比如GC停頓等。
binder_driver: Binder驅(qū)動(dòng)的相關(guān)信息,如果你懷疑是Binder IPC的問(wèn)題,不妨打開(kāi)這個(gè)。
core_services: SystemServer中系統(tǒng)核心Service的相關(guān)信息,分析特定問(wèn)題用

2.線程狀態(tài)查看
如下圖所示位置會(huì)顯示線程的執(zhí)行情況:
綠色:Running 線程正在完成與進(jìn)程相關(guān)的工作或正在響應(yīng)中斷。
藍(lán)色:Runnable 線程可以運(yùn)行但當(dāng)前沒(méi)有安排。
白色:Sleeping 線程沒(méi)有工作要做,可能是因?yàn)榫€程在互斥鎖上被阻塞。
橙色:Uninterruptable sleep(不間斷的睡眠) 線程在I/O上被阻塞或等待磁盤(pán)操作完成。
紫色:Interruptable sleep(可以中斷睡眠) 線程在另一個(gè)內(nèi)核操作(通常是內(nèi)存管理)上被阻塞。

3.相關(guān)時(shí)間解釋
Wall Duration 持續(xù)時(shí)間
CPU Duration CPU耗時(shí)
Self Time 自身方法耗時(shí)(不包含其調(diào)用方法
CPU Self Time 自身方法CPU執(zhí)行時(shí)間

擴(kuò)展:

1.Systrace中典型的一幀繪制流程

圖片22.png

這張圖對(duì)應(yīng)的工作流程如下:
1.主線程處于 Sleep 狀態(tài),等待 Vsync 信號(hào)
2.Vsync 信號(hào)到來(lái),主線程被喚醒,Choreographer 回調(diào) FrameDisplayEventReceiver.onVsync 開(kāi)始一幀的繪制
3.處理 App 這一幀的 Input 事件(如果有的話)
4.處理 App 這一幀的 Animation 事件(如果有的話)
5.處理 App 這一幀的 Traversal 事件(如果有的話)
6.主線程與渲染線程同步渲染數(shù)據(jù),同步結(jié)束后,主線程結(jié)束一幀的繪制,可以繼續(xù)處理下一個(gè) Message(如果有的話,IdleHandler 如果不為空,這時(shí)候也會(huì)觸發(fā)處理),或者進(jìn)入 Sleep 狀態(tài)等待下一個(gè) Vsync
7.渲染線程首先需要從 BufferQueue 里面取一個(gè) Buffer(dequeueBuffer) , 進(jìn)行數(shù)據(jù)處理之后,調(diào)用 OpenGL 相關(guān)的函數(shù),真正地進(jìn)行渲染操作,然后將這個(gè)渲染好的 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)點(diǎn)擊的段(這個(gè)比較常用,可以快速標(biāo)識(shí)出這個(gè)方法的左右邊界和執(zhí)行時(shí)間,方便上下查看)

鼠標(biāo)模式快捷切換 : 主要是針對(duì)鼠標(biāo)的工作模式進(jìn)行切換,默認(rèn)是 1,也就是選擇模式,查看 Systrace 的時(shí)候,需要經(jīng)常在各個(gè)模式之間切換,所以點(diǎn)擊切換模式效率比較低,直接用快捷鍵切換效率要高很多

數(shù)字鍵1 : 切換到 Selection 模式,這個(gè)模式下鼠標(biāo)可以點(diǎn)擊某一個(gè)段查看其詳細(xì)信息,一般打開(kāi) Systrace 默認(rèn)就是這個(gè)模式,也是最常用的一個(gè)模式,配合 M 和 ASDW 可以做基本的操作
數(shù)字鍵2 : 切換到 Pan 模式,這個(gè)模式下長(zhǎng)按鼠標(biāo)可以左右拖動(dòng), 有時(shí)候會(huì)用到
數(shù)字鍵3 : 切換到 Zoom 模式,這個(gè)模式下長(zhǎng)按鼠標(biāo)可以放大和縮小,有時(shí)候會(huì)用到
數(shù)字鍵4 : 切換到 Timing 模式,這個(gè)模式下主要是用來(lái)衡量時(shí)間的,比如選擇一個(gè)起點(diǎn),選擇一個(gè)終點(diǎn),查看起點(diǎn)和終點(diǎn)這中間的操作所花費(fèi)的時(shí)間。

參考:
systrace常識(shí):
https://blog.csdn.net/f2006116/article/details/107581327

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 保證應(yīng)用流暢度,是指保證應(yīng)用在使用過(guò)程中能持續(xù)提供每秒60幀的運(yùn)行態(tài)。如果低于60幀每秒,就會(huì)出現(xiàn)視覺(jué)上的卡頓效果...
    cherishyan閱讀 5,036評(píng)論 1 5
  • 太長(zhǎng)不看版:在 Android UI 布局過(guò)程中,遵守一些慣用、有效的布局原則,可以制作出高效且復(fù)用性高的 UI。...
    Mupceet閱讀 4,026評(píng)論 0 14
  • 目錄 1.卡頓簡(jiǎn)介 2.檢測(cè)Jank:介紹監(jiān)測(cè)卡頓的方法 3.修復(fù)卡頓問(wèn)題:介紹如何修復(fù)卡頓問(wèn)題; 4.卡頓通常的...
    htkeepmoving閱讀 9,369評(píng)論 0 5
  • 注意事項(xiàng): 布局優(yōu)化;盡量使用include、merge、ViewStub標(biāo)簽,盡量不存在冗余嵌套及過(guò)于復(fù)雜布局(...
    HarryXR閱讀 5,355評(píng)論 1 19
  • 我是黑夜里大雨紛飛的人啊 1 “又到一年六月,有人笑有人哭,有人歡樂(lè)有人憂(yōu)愁,有人驚喜有人失落,有的覺(jué)得收獲滿(mǎn)滿(mǎn)有...
    陌忘宇閱讀 8,834評(píng)論 28 54

友情鏈接更多精彩內(nèi)容