Android 性能優(yōu)化系列 - 01 手把手教你使用 systrace

catalog.png

一. 概述

性能優(yōu)化是 Android 中的一個(gè)重要知識(shí),也是衡量一個(gè) Android 工程師水平的重要依據(jù),簡(jiǎn)單的性能優(yōu)化,可能很多人都會(huì)。比如以下幾個(gè)優(yōu)化 UI 渲染的方法,想必很多人都知道

  1. 使用“設(shè)置 --> 開發(fā)者選項(xiàng) --> 調(diào)試 GPU 過度繪制”,根據(jù)屏幕顯示的不同顏色來區(qū)分是存在過度繪制,從而排查該界面的 xml 文件,去除不必要的 background,消除過度繪制

  2. 通過 Layout Inspector 查看布局層級(jí),排查是否存在多層無用的嵌套(由于 Hierarchy Viewer 已經(jīng)被廢棄,如果使用 3.1 及更新版本的 Android Studio,使用 Layout Inspector 查看布局會(huì)更加方便)

  3. 在 xml 中使用 ViewStub & merge 標(biāo)簽,優(yōu)化布局層級(jí)

  4. ......

上面的這些點(diǎn)當(dāng)然很重要,但是在某種程度下,上面的這些做法已經(jīng)力不從心了,我們需要通過其他方式來達(dá)到優(yōu)化性能的目的

俗話說的好,工欲善其事,必先利其器,使用一個(gè)好的工具當(dāng)然可以讓我們事半功倍,由于 TraceView 過于嚴(yán)重的運(yùn)行時(shí)開銷,使得 TraceView 測(cè)量的很多數(shù)據(jù)偏差較大,所以 Google 現(xiàn)在強(qiáng)推 systrace,systrace 是一個(gè)非常強(qiáng)大的性能分析工具。

systrace 可以從系統(tǒng)層面上,收集并分析設(shè)備運(yùn)行時(shí)的所有進(jìn)程的時(shí)間信息,它從 Android 內(nèi)核中,比如:CPU 調(diào)度、磁盤活動(dòng)和 app 線程中收集信息,然后生成如下圖所示的 html 文件,需要說明的是:生成的 trace.html 文件必須用 Chrome 瀏覽器打開才可以正常的瀏覽使用.

overview.png

圖片來源:systrace。如上圖所示,‘Frames’ 那一行里面的每一個(gè)小圓圈就代表著每一幀,用不同的顏色來代表是否正常的渲染,如果某一個(gè)小圓圈用黃色/紅色表示,則表明這一幀的渲染可能存在問題

好,接下來我們來看下如何使用 systrace 工具

二. 如何使用

我使用的 Mac 電腦,所以以下操作都是在 Mac 上進(jìn)行的,在 Windows 系統(tǒng)上應(yīng)該也大同小異。

2.1 準(zhǔn)備工作

在使用 systrace 之前,需要做以下幾個(gè)準(zhǔn)備工作

  1. 較新的 Android SDK Tools

  2. 需要 PC 端配合,PC 端安裝了 Python 且配置在了系統(tǒng)環(huán)境變量中

  3. 調(diào)試的設(shè)備需要是 4.3(API Level 18)以上的,系統(tǒng)越高,可以收集到的信息越多,越有利于分析,分析的應(yīng)用需要是 debug 包

  4. 通過 usb 將 Android 設(shè)備和 PC 連接成功,處于可調(diào)試的狀態(tài)

至此,準(zhǔn)備工作已完成

2.2 使用

2.2.1 使用方法

通過 Terminal 進(jìn)入到 /Android/sdk/platform-tools/systrace/ 目錄下

systrace-sdk.png

這個(gè)時(shí)候,在設(shè)備上操作應(yīng)用,使應(yīng)用進(jìn)入到待調(diào)試的狀態(tài),比如需要調(diào)試某個(gè)頁(yè)面 RecyclerView ,則進(jìn)入該頁(yè)面

然后在 Terminal 里面運(yùn)行如下命令,其中 [options] [categories] 都是需要輸入的參數(shù)

./systrace.py [options] [categories]

比如,執(zhí)行如下命令,其中 -o mynewtrace.html -t 10 屬于 [options] 參數(shù),sched freq idle am wm gfx view binder_driver hal dalvik camera input res 屬于 [categories] 參數(shù)

./systrace.py -o mynewtrace.html -t 10 sched freq idle am wm gfx view binder_driver hal dalvik camera input res

在運(yùn)行 10s 之后,就會(huì)將記錄的設(shè)備活動(dòng)生成一個(gè)名為 mynewtrace.html 文件。

2.2.2 參數(shù)說明

那么 [options][categories] 都包括哪些參數(shù)呢?

[options] 參數(shù)是固定的,常用的包括以下幾個(gè)

options 縮寫 含義
-o <file> 指定輸出的文件,如:-o mynewtrace.html。如果沒有指定此參數(shù),文件默認(rèn)名稱是 trace.html
--time=<T> -t <T> 指定 systrace 的持續(xù)時(shí)間,如 -t 10,表示記錄 10s 鐘,<T>的單位是 s 秒。如果沒有指定此參數(shù),在按下回車鍵 Enter 健時(shí)結(jié)束 systrace
--buf-size=<N> -b <N> 指定 systrace 的 buffer 是 N kb。指定在 systrace 過程中,收集的數(shù)據(jù)的總?cè)萘?/td>
--app=<app-name> -a <app-name> 指定特定的應(yīng)用,比如:-a com.lijiankun24.shadowlayout。如果在此應(yīng)用中使用了 Trace.beginSection("tag")Trace.endSection,默認(rèn)情況下,這些標(biāo)簽是不會(huì)生效的,除非你通過此命令指定該應(yīng)用,在 systrace 輸出的 html 文件中才會(huì)記錄該標(biāo)簽標(biāo)記的方法的信息

在介紹 [categories] 參數(shù)之前,先介紹 systrace 兩個(gè)有用的指令

Global options 縮寫 含義
--help -h 查看幫助信息
--list-categories -l 因?yàn)椴煌脑O(shè)備,Android 系統(tǒng)版本也不一樣,支持的 [categories] 參數(shù)也不同。可以通過此命令查看連接的設(shè)備支持哪些 [categories] 參數(shù)

比如,我設(shè)備的 Android 系統(tǒng)是 Android 7.1.2,運(yùn)行如下命令以后,可以得到如下圖所示的 [categories] 參數(shù)信息

./systrace -l
systrace-l.png

上面的這些 [categories] 參數(shù)指明此設(shè)備支持哪些可以被記錄的模塊,常用的有以下幾個(gè)模塊

categories 全稱 含義
sched CPU Scheduling CPU 的調(diào)度信息,可以看到 CPU 的每個(gè)核在具體的時(shí)間點(diǎn)執(zhí)行了什么線程
gfx Graphics Graphics 渲染系統(tǒng),包括 SurfaceFlinger、VSync、Texture、RenderThread 的信息
input Input 輸入事件系統(tǒng),記錄鍵盤輸入、觸摸等事件信息
view View System View 視圖系統(tǒng),常見的 View 的 onMeasure、onLayout、onDraw 都記錄在此系統(tǒng)中
wm Window Manager WindowManager 的調(diào)用信息記錄在此模塊中
am Activity Manager ActivityManager 的調(diào)用信息記錄在此模塊中
dalvik Dalvik VM 虛擬機(jī)相關(guān)信息,比如 GC 垃圾回收信息

生成 trace.html 文件大概就是這樣,并不復(fù)雜,下面介紹幾個(gè)查看此 html 文件的快捷鍵,通過下面幾個(gè)常用的快捷鍵,可以方便的查看 html 文件

快捷鍵 含義
W 放大時(shí)間軸
S 放大時(shí)間軸
A 左移時(shí)間軸
D 右移時(shí)間軸
Right Arrow 選中所選時(shí)間軸上的下一個(gè)事件
Left Arrow 選中所選時(shí)間軸上的上一個(gè)事件

三. 分析 trace.html

3.1 簡(jiǎn)單分析 trace.html 文件

我們?cè)撊绾畏治錾傻?trace.html 文件呢?

如下圖所示,是一個(gè)放大后的 trace.html 的局部圖。我們都知道 Android 系統(tǒng)中的 60 fps 概念,也就是 1s 內(nèi)會(huì)渲染 60 幀,渲染一幀需要 16.6 ms,下圖中用紅色框起來的就是每一個(gè) frame,如果在 16.6 ms 內(nèi)完成了渲染,則該幀是綠色的,如果渲染超過了 16.6 ms,則呈現(xiàn)出黃色或者紅色

analyze1.png

圖片來源 systrace

在上圖中,選中存在問題的黃色幀以后,需要注意兩部分,如下所示

  1. 第一個(gè)紅色框中,高亮的部分是這一幀在 UI 線程和 RenderThread 線程中都調(diào)用了哪些方法

  2. 第二個(gè)紅色框中,展示了一些信息,包括非常有用的該幀出問題的原因(Alert & Description),這些都是系統(tǒng)給出的存在的問題和優(yōu)化建議

analyze2.png

如果我們?cè)谏蠄D中,選中右上角的 Alerts tab,會(huì)出現(xiàn)如下圖所示的信息,它告訴我們?cè)谶@段時(shí)間內(nèi)該問題出現(xiàn)的頻次,比如下圖所示的:Inefficient ListView recycling/rebinding 共出現(xiàn)了 55 次。

可以把 Alerts tab 當(dāng)做一個(gè)需要處理的 bug 列表,這個(gè)列表中的問題都不同程度上的對(duì)我們的幀渲染造成了問題。有時(shí)候可能只是幾行代碼的微小改動(dòng)和優(yōu)化,卻可以優(yōu)化我們很多的問題

analyze3.png

3.2 為自己的應(yīng)用添加 Trace 信息

默認(rèn)情況下,systrace 都只能記錄、收集系統(tǒng)層面的信息,比如 WindowManagerActivityManager、以及 Dalvik 等等模塊的,有沒有什么辦法也記錄收集自己應(yīng)用中的一些信息呢?

Android 是提供了這樣的 Api 的,這個(gè)類是 Trace 類,使用 Trace 類記錄自己應(yīng)用中的信息其實(shí)并不難,如下所示,有如下幾點(diǎn)需要注意

  1. Trace.beginSection(String sectionName)Trace.endSection() 需要成對(duì)出現(xiàn),為保證每個(gè) Trace.beginSection(String sectionName) 都會(huì)有對(duì)應(yīng)的 Trace.endSection(),建議使用 try {……} finally {……}

  2. 如果在 Trace.endSection() 之前有多個(gè) Trace.beginSection(String sectionName)Trace.endSection() 會(huì)匹配離它最近的一個(gè)未匹配過的 Trace.beginSection(String sectionName)

  3. Trace.beginSection(String sectionName)Trace.endSection() 需要在同一線程中

public class CardViewListActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Trace.beginSection("CardViewListActivity_onCreate");
        try {
            setContentView(R.layout.activity_card_view_list);
            RecyclerView recyclerView = findViewById(R.id.rv_card_view);
            recyclerView.setLayoutManager(new LinearLayoutManager(CardViewListActivity.this));
            recyclerView.setAdapter(new CardViewListAdapter());
        } finally {
            Trace.endSection();
        }
    }

    private static class CardViewListAdapter extends RecyclerView.Adapter<CardViewHolder> {

        @NonNull
        @Override
        public CardViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
            Trace.beginSection("CardViewListAdapter_onCreateViewHolder");
            CardViewHolder viewHolder;
            try {
                View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_card_view_list, null);
                Trace.beginSection("CardViewListAdapter_onCreateViewHolder_newHolder");
                try {
                    viewHolder = new CardViewHolder(view);
                } finally {
                    Trace.endSection();
                }
            } finally {
                Trace.endSection();
            }
            return viewHolder;
        }

        ……
    }

    ……

}    

在自己應(yīng)用的代碼中添加如上代碼之后并沒有結(jié)束,還有一點(diǎn)非常重要,在執(zhí)行 systrace 命令的時(shí)候,需要通過 -a <package_name> 指定應(yīng)用包名,這樣才會(huì)記錄、收集到自己應(yīng)用中添加的 trace 信息,如下所示:

./systrace.py -t 10 -o mytrace.html -a com.lijiankun24.shadowlayout sched freq idle am wm gfx view binder_driver hal dalvik camera input res

在生成的 trace.html 文件中,可以通過右上角的查找,找到 sectionName,就可以查到該 Trace 的記錄信息

image.png

3.3 原理淺析

其實(shí) systrace 的思想很簡(jiǎn)單,就是在一些關(guān)鍵路徑中打 log,通過 log 的開始和結(jié)束就可以得到一個(gè)方法的執(zhí)行時(shí)間信息,然后將這些 log 收集起來,就可以得到關(guān)鍵路徑的運(yùn)行時(shí)間信息,進(jìn)而得到整個(gè)系統(tǒng)的運(yùn)行性能信息。

在 Android 應(yīng)用、Android Framework 和 native 層通過不同的方法或類打 log

3.3.1 Android Framework

import android.os.Trace;
Trace.traceBegin(long traceTag, String methodName)
Trace.traceEnd(long traceTag)

比如在 ActivityThread 中的內(nèi)部類 H.handleMessage(Message msg) 方法如下所示

image.png

在 Android Framework 中是通過 Trace.traceBegin(long traceTag, String methodName) 方法打 log 的,傳入的 traceTag 是 Trace 類中的常量類,如下所示

image.png

其實(shí)這里的 Trace 常量值,和我們?cè)趫?zhí)行 ./systrace [options] [categories] 時(shí),傳入的 [categories] 值對(duì)應(yīng)的

3.3.2 Android 應(yīng)用

對(duì)應(yīng)的 traceTag 名稱是 TRACE_TAG_APP,在使用 systrace.py 命令運(yùn)行時(shí),需要通過 -a <package-name> 指定應(yīng)用的包名,才可以收集到埋的 tag

import android.os.Trace;
Trace.beginSection(String sectionName)
Trace.EndSection()

Trace 類的源碼如下,可見 traceBegin(long traceTag, String methodName)traceEnd(long traceTag)beginSection(String sectionName)endSection() 最后都調(diào)用了 native 方法 nativeTraceBegin(long tag, String name)nativeTraceEnd(long tag)

public final class Trace {

    @FastNative
    private static native void nativeTraceBegin(long tag, String name);
    @FastNative
    private static native void nativeTraceEnd(long tag);

    private Trace() {
    }

    ......

    public static void traceBegin(long traceTag, String methodName) {
        if (isTagEnabled(traceTag)) {
            nativeTraceBegin(traceTag, methodName);
        }
    }

    public static void traceEnd(long traceTag) {
        if (isTagEnabled(traceTag)) {
            nativeTraceEnd(traceTag);
        }
    }

    ......

    public static void beginSection(String sectionName) {
        if (isTagEnabled(TRACE_TAG_APP)) {
            if (sectionName.length() > MAX_SECTION_NAME_LEN) {
                throw new IllegalArgumentException("sectionName is too long");
            }
            nativeTraceBegin(TRACE_TAG_APP, sectionName);
        }
    }

    public static void endSection() {
        if (isTagEnabled(TRACE_TAG_APP)) {
            nativeTraceEnd(TRACE_TAG_APP);
        }
    }
}

3.3.3 native 層

其實(shí) systrace 本質(zhì)上是對(duì)其他工具的封裝,包括 PC 端的 atrace 和設(shè)備端的 ftraceftrace 是 Linux 內(nèi)核中的主要跟蹤機(jī)制。systrace 使用 atrace 開啟追蹤,然后讀取 ftrace 的緩存,并且把它重新轉(zhuǎn)換成HTML格式

#include<utils/Trace.h>
ATRACE_CALL();

這里介紹了 systrace 基本的分析方法和基本原理,更深入的分析方法和原理會(huì)在后面系列中更新,敬請(qǐng)期待.

四. 參考

https://developer.android.com/studio/command-line/systrace

http://gityuan.com/2016/01/17/systrace/

http://www.lxweimin.com/p/b492140a555f

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,663評(píng)論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,125評(píng)論 3 414
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,506評(píng)論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,614評(píng)論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,402評(píng)論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 54,934評(píng)論 1 321
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,021評(píng)論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,168評(píng)論 0 287
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,690評(píng)論 1 333
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,596評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,784評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,288評(píng)論 5 357
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,027評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,404評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,662評(píng)論 1 280
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,398評(píng)論 3 390
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,743評(píng)論 2 370

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