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

catalog.png

一. 概述

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

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

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

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

  4. ......

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

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

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

overview.png

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

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

二. 如何使用

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

2.1 準(zhǔn)備工作

在使用 systrace 之前,需要做以下幾個準(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 進入到 /Android/sdk/platform-tools/systrace/ 目錄下

systrace-sdk.png

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

然后在 Terminal 里面運行如下命令,其中 [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

在運行 10s 之后轨帜,就會將記錄的設(shè)備活動生成一個名為 mynewtrace.html 文件魄咕。

2.2.2 參數(shù)說明

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

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

options 縮寫 含義
-o <file> 指定輸出的文件,如:-o mynewtrace.html烟具。如果沒有指定此參數(shù)梢什,文件默認(rèn)名稱是 trace.html
--time=<T> -t <T> 指定 systrace 的持續(xù)時間,如 -t 10朝聋,表示記錄 10s 鐘嗡午,<T>的單位是 s 秒。如果沒有指定此參數(shù)冀痕,在按下回車鍵 Enter 健時結(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)簽是不會生效的吨拗,除非你通過此命令指定該應(yīng)用,在 systrace 輸出的 html 文件中才會記錄該標(biāo)簽標(biāo)記的方法的信息

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

Global options 縮寫 含義
--help -h 查看幫助信息
--list-categories -l 因為不同的設(shè)備劝篷,Android 系統(tǒng)版本也不一樣哨鸭,支持的 [categories] 參數(shù)也不同〗考耍可以通過此命令查看連接的設(shè)備支持哪些 [categories] 參數(shù)

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

./systrace -l
systrace-l.png

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

categories 全稱 含義
sched CPU Scheduling CPU 的調(diào)度信息,可以看到 CPU 的每個核在具體的時間點執(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 虛擬機相關(guān)信息幢泼,比如 GC 垃圾回收信息

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

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

三. 分析 trace.html

3.1 簡單分析 trace.html 文件

我們該如何分析生成的 trace.html 文件呢?

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

analyze1.png

圖片來源 systrace

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

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

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

analyze2.png

如果我們在上圖中承冰,選中右上角的 Alerts tab华弓,會出現(xiàn)如下圖所示的信息,它告訴我們在這段時間內(nèi)該問題出現(xiàn)的頻次困乒,比如下圖所示的:Inefficient ListView recycling/rebinding 共出現(xiàn)了 55 次寂屏。

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

analyze3.png

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

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

Android 是提供了這樣的 Api 的啄刹,這個類是 Trace 類涮坐,使用 Trace 類記錄自己應(yīng)用中的信息其實并不難,如下所示誓军,有如下幾點需要注意

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

  2. 如果在 Trace.endSection() 之前有多個 Trace.beginSection(String sectionName)昵时,Trace.endSection() 會匹配離它最近的一個未匹配過的 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é)束捷雕,還有一點非常重要,在執(zhí)行 systrace 命令的時候壹甥,需要通過 -a <package_name> 指定應(yīng)用包名救巷,這樣才會記錄、收集到自己應(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 原理淺析

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

在 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

其實這里的 Trace 常量值晴氨,和我們在執(zhí)行 ./systrace [options] [categories] 時,傳入的 [categories] 值對應(yīng)的

3.3.2 Android 應(yīng)用

對應(yīng)的 traceTag 名稱是 TRACE_TAG_APP碉输,在使用 systrace.py 命令運行時籽前,需要通過 -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 層

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

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

這里介紹了 systrace 基本的分析方法和基本原理辆毡,更深入的分析方法和原理會在后面系列中更新菜秦,敬請期待.

四. 參考

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

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

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市舶掖,隨后出現(xiàn)的幾起案子球昨,更是在濱河造成了極大的恐慌,老刑警劉巖眨攘,帶你破解...
    沈念sama閱讀 206,839評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件主慰,死亡現(xiàn)場離奇詭異,居然都是意外死亡鲫售,警方通過查閱死者的電腦和手機共螺,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來情竹,“玉大人藐不,你說我怎么就攤上這事∏匦В” “怎么了雏蛮?”我有些...
    開封第一講書人閱讀 153,116評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長阱州。 經(jīng)常有香客問我挑秉,道長,這世上最難降的妖魔是什么苔货? 我笑而不...
    開封第一講書人閱讀 55,371評論 1 279
  • 正文 為了忘掉前任犀概,我火速辦了婚禮立哑,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘姻灶。我一直安慰自己铛绰,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,384評論 5 374
  • 文/花漫 我一把揭開白布产喉。 她就那樣靜靜地躺著至耻,像睡著了一般。 火紅的嫁衣襯著肌膚如雪镊叁。 梳的紋絲不亂的頭發(fā)上尘颓,一...
    開封第一講書人閱讀 49,111評論 1 285
  • 那天,我揣著相機與錄音晦譬,去河邊找鬼疤苹。 笑死,一個胖子當(dāng)著我的面吹牛敛腌,可吹牛的內(nèi)容都是我干的卧土。 我是一名探鬼主播,決...
    沈念sama閱讀 38,416評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼像樊,長吁一口氣:“原來是場噩夢啊……” “哼尤莺!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起生棍,我...
    開封第一講書人閱讀 37,053評論 0 259
  • 序言:老撾萬榮一對情侶失蹤颤霎,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后涂滴,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體友酱,經(jīng)...
    沈念sama閱讀 43,558評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,007評論 2 325
  • 正文 我和宋清朗相戀三年柔纵,在試婚紗的時候發(fā)現(xiàn)自己被綠了缔杉。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,117評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡搁料,死狀恐怖或详,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情郭计,我是刑警寧澤霸琴,帶...
    沈念sama閱讀 33,756評論 4 324
  • 正文 年R本政府宣布,位于F島的核電站拣宏,受9級特大地震影響沈贝,放射性物質(zhì)發(fā)生泄漏杠人。R本人自食惡果不足惜勋乾,卻給世界環(huán)境...
    茶點故事閱讀 39,324評論 3 307
  • 文/蒙蒙 一宋下、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧辑莫,春花似錦学歧、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至揭蜒,卻和暖如春横浑,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背屉更。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評論 1 262
  • 我被黑心中介騙來泰國打工徙融, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人瑰谜。 一個月前我還...
    沈念sama閱讀 45,578評論 2 355
  • 正文 我出身青樓欺冀,卻偏偏與公主長得像,于是被迫代替她去往敵國和親萨脑。 傳聞我的和親對象是個殘疾皇子隐轩,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,877評論 2 345

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