Android性能工具——Systrace使用

Android性能工具——Systrace使用

一、屏幕刷新機制

基礎(chǔ)概念

在一個典型的顯示系統(tǒng)中蝉揍,一般包括CPU、GPU畦娄、display三個部分又沾, CPU負(fù)責(zé)計算數(shù)據(jù)弊仪,把計算好數(shù)據(jù)交給GPU,GPU會對圖形數(shù)據(jù)進(jìn)行渲染,渲染好后放到buffer里存起來杖刷,然后display(有的文章也叫屏幕或者顯示器)負(fù)責(zé)把buffer里的數(shù)據(jù)呈現(xiàn)到屏幕上励饵。
顯示過程,簡單的說就是CPU/GPU準(zhǔn)備好數(shù)據(jù)滑燃,存入buffer役听,display每隔一段時間去buffer里取數(shù)據(jù),然后顯示出來不瓶。display讀取的頻率是固定的禾嫉,比如每個16ms讀一次,但是CPU/GPU寫數(shù)據(jù)是完全無規(guī)律的蚊丐。

簡單的說熙参,屏幕的刷新包括三個步驟:CPU 計算屏幕數(shù)據(jù)、GPU 進(jìn)一步處理和緩存麦备、最后 display 再將緩存中(buffer)的屏幕數(shù)據(jù)顯示出來孽椰。

Screen Tearing(撕裂)

由于display處理的頻率是固定的,而CPU/GPU處理數(shù)據(jù)的時間是不確定的凛篙,因此在早期的設(shè)備上黍匾,由于是單緩沖的模式,則會有屏幕撕裂的情況發(fā)生呛梆。

image

例如顯示周期為0.01秒锐涯,則在0.01秒時顯示正確,而在0.01秒-0.02秒時填物,CPU/GPU僅完成了部分工作纹腌,則在0.02秒時,屏幕顯示的是上部分為2滞磺,下部分為1的撕裂畫面升薯。

Double-Buffer

雙緩沖技術(shù),基本原理就是采用兩塊buffer击困。一塊back buffer用于CPU/GPU后臺繪制涎劈,另一塊framebuffer則用于顯示,當(dāng)back buffer準(zhǔn)備就緒后阅茶,它們才進(jìn)行交換蛛枚。

為了避免Tearing情況發(fā)生,當(dāng)掃描完一個屏幕后脸哀,設(shè)備需要重新回到第一行以進(jìn)入下一次的循環(huán)坤候,此時有一段時間空隙,這個時間點就是我們進(jìn)行緩沖區(qū)交換的最佳時間企蹭,VSync信號也是在這個時間點產(chǎn)生的白筹。

VSync

在android4.1之前智末,沒有采用Vsync信號時,CPU/GPU往Buffer里面寫數(shù)據(jù)是比較隨意的徒河,CPU/GPU開始工作的時間不是固定的系馆,而Display處理的頻率是固定的,因此顽照,會造成丟幀(Jank)的情況發(fā)生由蘑,如下圖所示。

image

在android4.1之后代兵,對Android Display系統(tǒng)進(jìn)行了重構(gòu)尼酿,實現(xiàn)了Project Butter,引入了三個核心元素植影,即VSYNC裳擎、Triple Buffer和Choreographer。
Project Butter規(guī)定系統(tǒng)一旦收到vsync通知(16ms觸發(fā)一次),CPU和GPU就立刻開始工作把顯示數(shù)據(jù)寫入buffer思币。

系統(tǒng)規(guī)定收到Vsync信號后鹿响,CPU就開始處理屏幕繪制數(shù)據(jù),CPU/GPU根據(jù)VSYNC信號同步處理數(shù)據(jù)谷饿,可以讓CPU/GPU有完整的16ms時間來處理數(shù)據(jù)惶我,減少了jank。

Triple Buffer

雙緩存的機制并不是完美的博投,比如當(dāng)CPU/GPU工作時間較長時绸贡,會發(fā)生如下情況:

image

當(dāng)CPU/GPU的處理時間超過16ms時,第一個VSync到來時毅哗,緩沖區(qū)B中的數(shù)據(jù)還沒有準(zhǔn)備好听怕,于是只能繼續(xù)顯示之前A緩沖區(qū)中的內(nèi)容。而B完成后黎做,又因為缺乏VSync信號叉跛,它只能等待下一個信號的來臨松忍。于是在這一過程中蒸殿,有一大段時間是被浪費的。
當(dāng)下一個VSync出現(xiàn)時鸣峭,CPU/GPU馬上執(zhí)行操作宏所,此時它可操作的buffer是A,相應(yīng)的顯示屏對應(yīng)的就是B摊溶。這時看起來就是正常的爬骤。只不過由于執(zhí)行時間仍然超過16ms,導(dǎo)致下一次應(yīng)該執(zhí)行的緩沖區(qū)交換又被推遲了——如此循環(huán)反復(fù)莫换,便出現(xiàn)了越來越多的Jank霞玄。

image

三緩存不能解決雙緩沖帶來的第一次Jank丟失問題骤铃,但是當(dāng)?shù)谝淮蜼Sync發(fā)生后,CPU不用再等待了坷剧,它會使用第三個buffer C來進(jìn)行下一幀數(shù)據(jù)的準(zhǔn)備工作惰爬。雖然對緩沖區(qū)C的處理所需時間同樣超過了16ms,但這并不影響顯示屏——第2次VSync到來后惫企,它選擇buffer B進(jìn)行顯示;而第3次VSync時撕瞧,它會接著采用C,而不是像double buffering中所看到的情況一樣只能再顯示一遍B了狞尔。這樣子就有效地降低了jank丛版。

二、App相關(guān)的屏幕繪制

App刷新屏幕流程

  1. 界面上任何一個 View 的刷新請求最終都會走到 ViewRootImpl 中的 scheduleTraversals() 里來安排一次遍歷繪制 View 樹的任務(wù)偏序;
  2. scheduleTraversals() 會先過濾掉同一幀內(nèi)的重復(fù)調(diào)用页畦,在同一幀內(nèi)只需要安排一次遍歷繪制 View 樹的任務(wù)即可,這個任務(wù)會在下一個屏幕刷新信號到來時調(diào)用 performTraversals() 遍歷 View 樹禽车,遍歷過程中會將所有需要刷新的 View 進(jìn)行重繪寇漫;
  3. 接著 scheduleTraversals() 會往主線程的消息隊列中發(fā)送一個同步屏障,攔截這個時刻之后所有的同步消息的執(zhí)行殉摔,但不會攔截異步消息州胳,以此來盡可能的保證當(dāng)接收到屏幕刷新信號時可以盡可能第一時間處理遍歷繪制 View 樹的工作;
  4. 發(fā)完同步屏障后 scheduleTraversals() 才會開始安排一個遍歷繪制 View 樹的操作逸月,作法是把 performTraversals() 封裝到 Runnable 里面栓撞,然后調(diào)用 Choreographer 的 postCallback() 方法;
  5. postCallback() 方法會先將這個 Runnable 任務(wù)以當(dāng)前時間戳放進(jìn)一個待執(zhí)行的隊列里碗硬,然后如果當(dāng)前是在主線程就會直接調(diào)用一個native 層方法瓤湘,如果不是在主線程,會發(fā)一個最高優(yōu)先級的 message 到主線程恩尾,讓主線程第一時間調(diào)用這個 native 層的方法弛说;
  6. native 層的這個方法是用來向底層注冊監(jiān)聽下一個屏幕刷新信號,當(dāng)下一個屏幕刷新信號發(fā)出時翰意,底層就會回調(diào) Choreographer 的onVsync() 方法來通知上層 app木人;
  7. onVsync() 方法被回調(diào)時,會往主線程的消息隊列中發(fā)送一個執(zhí)行 doFrame() 方法的消息冀偶,這個消息是異步消息醒第,所以不會被同步屏障攔截住进鸠;
  8. doFrame() 方法會去取出之前放進(jìn)待執(zhí)行隊列里的任務(wù)來執(zhí)行稠曼,取出來的這個任務(wù)實際上是 ViewRootImpl 的 doTraversal() 操作;
  9. 上述第4步到第8步涉及到的消息都手動設(shè)置成了異步消息客年,所以不會受到同步屏障的攔截霞幅;
  10. doTraversal() 方法會先移除主線程的同步屏障漠吻,然后調(diào)用 performTraversals() 開始根據(jù)當(dāng)前狀態(tài)判斷是否需要執(zhí)行performMeasure() 測量、perfromLayout() 布局司恳、performDraw() 繪制流程侥猩,在這幾個流程中都會去遍歷 View 樹來刷新需要更新的View;

總結(jié)來說抵赢,當(dāng)有屏幕刷新操作時欺劳,系統(tǒng)會將View樹的測量、布局和繪制等封裝到一個Runnable铅鲤,然后監(jiān)聽VSync信號划提,等Vsync信號來時,再觸發(fā)此Runnable邢享。

三鹏往、卡頓分析利器——Systrace工具

簡介

Systrace是分析Android性能問題的神器,Google IO 2017上更是對其各種強推. 是分析卡頓掉幀問題核心工具骇塘,只要能提供卡頓現(xiàn)場伊履,systrace就能很好定位問題。

原理

在介紹使用之前款违,先簡單說明一下Systrace的原理:它的思想很樸素唐瀑,在系統(tǒng)的一些關(guān)鍵鏈路(比如System Service,虛擬機插爹,Binder驅(qū)動)插入一些信息(我這里稱之為Label)哄辣,通過Label的開始和結(jié)束來確定某個核心過程的執(zhí)行時間,然后把這些Label信息收集起來得到系統(tǒng)關(guān)鍵路徑的運行時間信息赠尾,進(jìn)而得到整個系統(tǒng)的運行性能信息力穗。Android Framework里面一些重要的模塊都插入了Label信息(Java層的通過android.os.Trace類完成,native層通過ATrace宏完成)气嫁,用戶App中可以添加自定義的Label当窗,這樣就組成了一個完成的性能分析系統(tǒng)。另外說明的是:Systrace對系統(tǒng)版本有一個要求寸宵,就是需要Android 4.1以上崖面。系統(tǒng)版本越高叔遂,Android Framework中添加的系統(tǒng)可用Label就越多,能夠支持和分析的系統(tǒng)模塊也就越多彰亥;因此香璃,在可能的情況下,盡可能使用高版本的Android系統(tǒng)來進(jìn)行分析苔货。

獲取systrace文件

要想分析卡頓現(xiàn)場,必須先獲取到卡頓現(xiàn)場的Systrace文件,獲取Systrace文件的方式有兩種救军,一種是通過AndroidSDK/tools目錄下财异,通過monitor.bat用Android Device Monitor可視化工具得到,一種是通過python腳本獲取唱遭,我本人更喜歡通過腳本獲取戳寸,因為更方便一點。

1. 通過Android Device Monitor獲取

  1. 點擊綠色按鈕啟動Systrace

    image

  2. 選擇抓取Systrace的配置文件

    image

    這里拷泽,我們僅僅抓取5秒鐘的系統(tǒng)數(shù)據(jù)疫鹊,沒有選擇特別的應(yīng)用進(jìn)程,抓取的內(nèi)容為基礎(chǔ)內(nèi)容司致。

  3. 點擊OK開始抓取

2. 通過python腳本抓炔疬骸(推薦)

  1. 裝python2.X版本,Systrace腳本不支持3.X版本脂矫。

  2. 通過python腳本執(zhí)行AndroidSDK\platform-tools\systrace\目錄下的systrace.py文件

  3. 可以配置一些參數(shù)枣耀,類似于通過Android Device Monitor抓取時步驟2配置的顯示信息,若不選擇則默認(rèn)全部抓取庭再。

  4. 配置一些其他實用參數(shù):

    • -o: 指定文件輸出位置和文件名
    • -t: 抓取systrace的時間長度
    • -a: 指定特殊進(jìn)程包名(自己加Label時必須加上)
  5. 抓取腳本示例:


    image

Systrace文件說明

準(zhǔn)備工具

  1. Chrome瀏覽器(必須)捞奕。
    在地址欄輸入chrome://tracing命令,然后將生成的trace.html文件拖進(jìn)來拄轻,或者通過load按鈕導(dǎo)入颅围。
    image

常用快捷鍵說明:

  • W: 放大橫軸,用于查看耗時方法細(xì)節(jié)恨搓;
  • S: 縮小橫軸谷浅,用于查看整體情況;
  • A: 將面板左移奶卓;
  • D: 將面板右移一疯;
  • M: 高亮某一段耗時內(nèi)容。

文件結(jié)構(gòu)

Systrace的文件結(jié)構(gòu)從上到下一般是:

  1. 內(nèi)核信息(CPU片信息)夺姑;

  2. SurfaceFlinger(底層繪制信號等信息)墩邀;

  3. system_server等其他進(jìn)程信息。

性能問題分析

一般用Systrace用來分析卡頓盏浙、啟動時間慢等問題眉睹,還可以用來分析方法耗時等。

1. 卡頓問題分析

首先分析一下正常的無卡頓時的表現(xiàn):

image

當(dāng)按W并且按M放大高亮每一幀細(xì)節(jié)的時候废膘,可以看到每一幀的繪制開始都是在16ms以內(nèi)完成的:

image

從某一幀的刷新竹海,我們也可以知道,刷新是從Choreographer的doFrame()開始的丐黄,一直到GPU線程繪制完畢結(jié)束斋配。

現(xiàn)在,我們修改代碼,在 ListView 的getView()方法延遲100ms艰争,然后抓取systrace坏瞄,查看一下耗時情況。

代碼:

    @Override
    public View getView(int i, View view, ViewGroup viewGroup) {
        SystemClock.sleep(100);
        if(view != null) {
            ((TextView)view).setText(list.get(i));
            return view;
        }
        TextView tv = new TextView(viewGroup.getContext());
        tv.setText(list.get(i));
        return tv;
    }
image

從圖上甩卓,我們可以看見用來標(biāo)識每一幀耗時的F標(biāo)志變成了紅色鸠匀,這說明這一幀是耗時特別嚴(yán)重的。

高亮某一個帶紅色F的幀:

image

從圖上逾柿,可以看出這一幀耗時達(dá)到548ms缀棍,其中耗時很大的部分為input,這個input和上面的deliverInputEvent相對應(yīng)机错,說明是觸摸事件耗時過長(其實是因為我們在主線程sleep了100ms導(dǎo)致的)睦柴。
為什么這一幀耗時是500ms呢?明明我們只sleep了100ms呀毡熏。原因是我們是在每一個ListView的getView()方法sleep了100ms坦敌,而這次觸摸事件刷新出了5個item,每一個item的getView()方法都耗時了100ms(obtainView()會調(diào)用getView())痢法,因此耗時達(dá)到了500多ms狱窘。

分析卡頓的步驟就是:

  1. 找到卡頓的場景,并抓取卡頓發(fā)生時的systrace文件财搁;

  2. 找到發(fā)生問題的應(yīng)用進(jìn)程的主線程蘸炸,并通過標(biāo)紅的F圖標(biāo),找到發(fā)生問題的問題幀尖奔;

  3. 通過放大和高亮去判斷具體是哪一個細(xì)節(jié)點發(fā)生了耗時情況搭儒。

若從systrace文件中看不出來具體是哪一個方法耗時,我們可以自己在代碼中加入Label提茁,去查看方法具體耗時(替代TraceView)淹禾,后面會講到。

2. 啟動時間分析

很多時候茴扁,我們對App的冷啟動時間都有一定的要求铃岔,我們當(dāng)然可以通過adb命令去啟動我們的MainActivity,然后查看啟動時間峭火,但是那并不是真正意義上應(yīng)用完全打開的啟動時間毁习,而通過Systrace則可以很方便的統(tǒng)計啟動時間,并找出耗時的地方卖丸。

這里纺且,我們統(tǒng)計耗時是從用戶點擊Launcher上的應(yīng)用圖標(biāo),到界面完全顯示的時間, 那么界面完全顯示的時間是哪里稍浆?有兩個方法载碌,一個是在onWindowFocusChange()方法中打印Label猜嘱,或者通過MessageQueue.IdelHandler來進(jìn)行,這里恐仑,簡單起見我們就用onWindowFocusChange()方法中加Label吧。

自己加Label主要是通過API:Trace.beginSection("名稱")Trace.endSection()來進(jìn)行为鳄,自己的Label有以下需要注意:

  • beginend必須成對出現(xiàn)裳仆;
  • Label支持嵌套;
  • beginend必須在同一個線程中孤钦;
  • 抓取systrace時歧斟,必須指定包名

代碼示例:

private boolean flag = true;
private static Handler mHandler = new Handler();
@Override
public void onWindowFocusChanged(boolean hasFocus) {
    super.onWindowFocusChanged(hasFocus);
    if(hasFocus && flag) {
        flag = false;
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                Trace.beginSection("endTime");
                SystemClock.sleep(500);
                Trace.endSection();
            }
        });
    }
}

systrace文件:

image

啟動時間計算:
1、找到Launcher進(jìn)程中最后一次手指按下的時間點 deliverInputEvent的標(biāo)示偏形,這里是起點:

image

2静袖、終點就是上圖中endTime()打印時開始的地方,用圖中標(biāo)示的工具俊扭,將兩段時間相連队橙,就能得到本次應(yīng)用的冷啟動時間為434ms。

image

總結(jié)

  • 屏幕繪制的流程為 CPU計算數(shù)據(jù) -> GPU繪制界面 -> 數(shù)據(jù)放入Buffer -> Display從Buffer中取數(shù)據(jù)呈現(xiàn)萨惑;

  • Display刷新的時間是固定的(Android中可以理解為16.6ms)捐康,而CPU/GPU計算數(shù)據(jù)的時間不固定,因此會有很多顯示問題(撕裂庸蔼、丟幀)解总;

  • Systrace可以查看丟幀現(xiàn)場的具體問題點(哪些方法耗時過長導(dǎo)致丟幀);

  • Systrace可以通過添加Label代替TraceView的作用姐仅,用來統(tǒng)計方法耗時詳情和應(yīng)用啟動時間花枫。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市掏膏,隨后出現(xiàn)的幾起案子劳翰,更是在濱河造成了極大的恐慌,老刑警劉巖馒疹,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件磕道,死亡現(xiàn)場離奇詭異,居然都是意外死亡行冰,警方通過查閱死者的電腦和手機溺蕉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來悼做,“玉大人疯特,你說我怎么就攤上這事「刈撸” “怎么了漓雅?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我邻吞,道長组题,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任抱冷,我火速辦了婚禮崔列,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘旺遮。我一直安慰自己赵讯,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布耿眉。 她就那樣靜靜地躺著边翼,像睡著了一般。 火紅的嫁衣襯著肌膚如雪鸣剪。 梳的紋絲不亂的頭發(fā)上组底,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天,我揣著相機與錄音筐骇,去河邊找鬼斤寇。 笑死,一個胖子當(dāng)著我的面吹牛拥褂,可吹牛的內(nèi)容都是我干的娘锁。 我是一名探鬼主播,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼饺鹃,長吁一口氣:“原來是場噩夢啊……” “哼莫秆!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起悔详,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤镊屎,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后茄螃,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體缝驳,經(jīng)...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年归苍,在試婚紗的時候發(fā)現(xiàn)自己被綠了用狱。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,090評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡拼弃,死狀恐怖夏伊,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情吻氧,我是刑警寧澤溺忧,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布咏连,位于F島的核電站,受9級特大地震影響鲁森,放射性物質(zhì)發(fā)生泄漏祟滴。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一歌溉、第九天 我趴在偏房一處隱蔽的房頂上張望垄懂。 院中可真熱鬧,春花似錦研底、人聲如沸埠偿。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至羽圃,卻和暖如春乾胶,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背朽寞。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工识窿, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人脑融。 一個月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓喻频,卻偏偏與公主長得像,于是被迫代替她去往敵國和親肘迎。 傳聞我的和親對象是個殘疾皇子甥温,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,033評論 2 355

推薦閱讀更多精彩內(nèi)容