APP冷啟動優(yōu)化:如何使用好工具【Perfetto\ systrace \MethodTracing】

APP的性能提升無非就是圍繞穩(wěn)定彬向、流暢之類的指標做文章,在推動性能提升的時候攻冷,什么才是關(guān)鍵娃胆,熱情?能力 等曼?規(guī)范里烦?,個人認為是工具涉兽,用好性能分析工具招驴,性能提升就走完了一大半篙程,就好比:”算數(shù)我比不過小王枷畏,但我找了個電子計算器“。以提升冷啟動速度為例虱饿,看看整體的性能優(yōu)化流程應該是什么樣子拥诡,而在這其中性能工具能帶來什么触趴。

冷啟動的定義與可優(yōu)化的點

如何衡量當前的性能指標,個人感覺渴肉,性能的衡量分三步: 指標制-> 指標采集 -> 性能基線與優(yōu)劣評級, 以上三塊組成性能量化工具冗懦,有了量化工具,就可以說APP性能是好是壞仇祭,以冷啟動為例披蕉,冷啟動指標如何制定?單從技術(shù)上說感覺可以定義如下:

冷啟動耗時 = 從APP進程創(chuàng)建到第一個有效頁面幀[閃屏]

具體到實現(xiàn)上乌奇,涉及哪些環(huán)節(jié)没讲,會怎樣影響冷啟動速度呢?

冷啟動->系統(tǒng)會啟動一個StartWindow占位-> 啟動進程->創(chuàng)建Application-?>Application中初始化全局配置->啟動第一個Activity->Create->Start->Resume->AddWindow->UI測量繪制[performTraversals]->首幀可見
image.png

冷啟動的時候礁苗,系統(tǒng)一般會先啟動一個占位Window爬凑,默認是個白屏窗口,復用的是第一個啟動Activity配置试伙,在體感上嘁信,主要下面的Activity配置

    <item name="android:windowBackground">@drawable/xxx</item>

它一般是SplashActivity的配置,用品牌圖做個中轉(zhuǎn)疏叨,這個圖最好要限制下尺寸潘靖,否則在解析上影響啟動速度。隨后系統(tǒng)會啟動進程加載SplashActivity蚤蔓,啟動進程主要是Application中可能有些APP全局初始化操作秘豹,盡量輕,或者延后處理昌粤,當然既绕,也會有一些ContentProvider與Receiver影響啟動這些都可以通過工具查看。

public class LabApplication extends Application {
    @Override
    public void onCreate() {
            super.onCreate()
            <!--UI中不要處理耗時操作-->
 }

之后便是Activity的創(chuàng)建與啟動流程 :

image.png

可以看到上圖的Activity啟動流程都是被動消息的處理涮坐,主要是受控AMS指揮凄贩,代碼中設(shè)置View及顯示的流程也就上圖的幾個點,比如onCreate中設(shè)置Layout并inflater袱讹,當然疲扎,這不是必須的,即使不主動setContentView捷雕,在后面的wm.addView中也會創(chuàng)建頂層DecorView椒丧。

@Override
public void setContentView(int resId) {
    ensureSubDecor();
    ViewGroup contentParent = mSubDecor.findViewById(android.R.id.content);
    contentParent.removeAllViews();
    <!--可能影響耗時-->
    LayoutInflater.from(mContext).inflate(resId, contentParent);
    mAppCompatWindowCallback.getWrapped().onContentChanged();
}

而setContentView的inflate可能是影響耗時的一個點。之后handleResumeActivity中救巷,會想WMS添加窗口View

@Override
    public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
            String reason) {
                ...
        final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);
                ...
            r.window = r.activity.getWindow();
                View decor = r.window.getDecorView();
                if (!a.mWindowAdded) {
                    a.mWindowAdded = true;
                    <!--添加Window-->
                    wm.addView(decor, l);
                } else {
                   
    @Override
    public final View getDecorView() {
        if (mDecor == null || mForceDecorInstall) {
            installDecor();
        }
        return mDecor;
    }

可以看到 getDecorView會兜底處理Activity 的頂層窗口創(chuàng)建邏輯壶熏。addView會調(diào)用WindowManagerGlobal的addView,進而創(chuàng)建ViewRootImpl浦译,利用ViewRootImpl進一步添加Window

   public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
              <!--關(guān)鍵-->
              root = new ViewRootImpl(view.getContext(), display);

            view.setLayoutParams(wparams);
                <!--添加到WMS棒假,處理UI顯示-->
                
                root.setView(view, wparams, panelParentView);
     
        }
    }

而ViewRootImpl接管流程之后溯职,所以View相關(guān)的操作都將在ViewRootImpl處理,而最終由其requestLayout觸發(fā)測量帽哑、布局谜酒、繪制的動作

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
    synchronized (this) {
        if (mView == null) {

                     <!--申請下個Message繪制-->
                    requestLayout();
                    
                <!--添加窗口-->      
                mOrigWindowType = mWindowAttributes.type;
                mAttachInfo.mRecomputeGlobalAttributes = true;
                collectViewAttributes();
                res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                        getHostVisibility(), mDisplay.getDisplayId(), mWinFrame,
                        mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                        mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel);
             
        }
    }
}

requestLayout會scheduleTraversals預先占位一個異步消息,用于接收并doScheduleCallback觸發(fā)的VSYNC信號妻枕,這樣可以保證之后插入的消息都被延期處理僻族,從而Window被添加后,UI繪制任務第一時間執(zhí)行屡谐。

void scheduleTraversals() {
    if (!mTraversalScheduled) {
        mTraversalScheduled = true;
        <!--異步消息-->
        mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
        <!--插入繪制請求鹰贵,觸發(fā)VSYNC-->
        mChoreographer.postCallback(
                Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);

}

等待VSYNC消息回來后,撤離異步消息柵欄康嘉,第一時間處理UI繪制:

image.png

所以首幀的渲染一定是在Resume之后碉输,那么具體的時機怎么把控?到底在哪亭珍,如下圖所示敷钾,插入的點在哪?

image.png

網(wǎng)上有一些其他的實現(xiàn)肄梨,認為可以監(jiān)聽onAttachedToWindow或者OnWindowFocusChange阻荒,onAttachedToWindow的問題是可能太過靠前,還沒有Draw, OnWindowFocusChange的缺點可能是太過滯后众羡,其實可以簡單認為view會的draw以后侨赡,View的繪制就算完成,雖然到展示還可能相差一個VSYNC等待圖層合成粱侣,但是對于性能監(jiān)測的評定羊壹,誤差一個固定值可以接受:

image.png

在onResume函數(shù)中插入一條消息可以嗎,理論上來說齐婴,太過靠前油猫,這條消息在執(zhí)行的時候,還沒Draw柠偶,因為請求VSYNC的同步柵欄是在是在Onresume結(jié)束后才插入的情妖,無法攔截之前的Message,但是由于VSYNC可能存在復用诱担,Onresume中插入的消息也有可能會在繪制之后執(zhí)行毡证,這個不是完全一定的,比如點擊MaterialButton啟動一個Activity蔫仙,第二個Activity的setView觸發(fā)的VSYNC就可能復用MaterialButton的波紋觸發(fā)的VSYNC料睛,從而導致第二個Activity的performTraval復用第一個VSYNC執(zhí)行,從而發(fā)生在onResume插入消息之前,如下

柵欄消息

image.png

重繪CallBack包含多個Activity的重繪

image.png

綜上所述秦效,將指標定義在第一次View的Draw執(zhí)行可能比較靠譜雏蛮。具體可以再DecorView上插入一個透明View涎嚼,監(jiān)聽器onDraw回調(diào)即可阱州,如果覺得不夠優(yōu)雅,就退一步法梯,監(jiān)聽OnWindowFocusChange的回調(diào)苔货,也勉強可以接受, OnWindowFocusChange一定是在Draw之后的。
有了指標立哑,那是否達標夜惭?如何采集?基線呢铛绰?可以參考業(yè)界做法诈茧,采集方式可以無入侵打點,而優(yōu)秀基線可以認為:

優(yōu)秀=秒開 

如果發(fā)現(xiàn)不達標捂掰,接下來要做的就是定位+優(yōu)化敢会,這個時候就體現(xiàn)分析工具的重要性,其實上述的原理分析就已經(jīng)借助Studio自帶的Profiler工具这嚣,在理解流程上事半功倍鸥昏。

如何定位當前性能問題

冷啟動每個階段的耗時可以通過多種工具、方式來定位:可以用的有Debug.startMethodTracing跟蹤姐帚,也可以利用perfetto/systrace來查看吏垮,甚至還可以用Studio自身的Profiler跟蹤,每種方式都有自己的優(yōu)勢罐旗,可配合選擇使用膳汪。

Debug.startMethodTracing 適合查看UI線程的耗時函數(shù)

Debug.startMethodTracing是通過應用插樁來生成跟蹤日志,做到對方法的跟蹤九秀。但是啟用剖析功能后旅敷,應用的運行速度會減慢,所以颤霎,不應使用剖析數(shù)據(jù)確定絕對時間媳谁,最大的作用是用在對比上,可以對比之前友酱,或者對比周圍函數(shù)晴音。具體用法:

private void startTrace() {
    File file = new File(getApplication().getExternalFilesDir("android"), "methods.trace");
    Debug.startMethodTracing(file.getAbsolutePath(), 100 * 1024 * 1024);
}
<!-注意配對使用-->
private void stopTrace() {
    Debug.stopMethodTracing();
}

對于冷啟動:進程啟動時開啟監(jiān)聽,在合適節(jié)點配對停止即可缔杉,之后導出.trace文件在Studio中分析锤躁,可以看到關(guān)鍵函數(shù)耗,Studio提供了多種模式或详,F(xiàn)lame Chart系羞、Top Down郭计、Bottom Up、Event椒振,不同的模式側(cè)重點不同昭伸。定向分析的時候,可以分段鎖定范圍澎迎,比如冷啟動可分幾個階段排查庐杨,進程創(chuàng)建、Application初始化夹供、Activity的創(chuàng)建灵份、create、resume哮洽、draw等填渠,先選定Main線程,然后將范圍限制定Application階段鸟辅,如下下:

  • Flame Chart:更側(cè)重直觀反映函數(shù)耗時嚴重程度
image.png

比如上圖氛什,淺黃色部分其實就是需要重點關(guān)注的部分,耗時最多的函數(shù),會最先展示剔桨,更加方便定位嚴重問題屉更,大致定位問題后,就可以用Top Down 進一步看細節(jié)洒缀。

  • Top Down: 更側(cè)重自頂向下詳細排查

利用Top-Down模式可以更精確觀察函數(shù)耗時與調(diào)用堆棧瑰谜,更加清晰,如下在Application初始化階段树绩,可以清醒看到函數(shù)調(diào)用順序萨脑、耗時等,

image.png

對于冷啟動饺饭,重點排查耗時函數(shù)渤早,嘗試將非核心邏輯從UI線程中移除。同理對于閃屏Activity的onCreate跟onResume階段所做的處理類似

image.png

從圖中就很容下發(fā)現(xiàn)瘫俊,有些Flutterboost鹊杖、埋點Json解析類的耗時操作被不小心關(guān)聯(lián)進了Activit的啟動流程中,拖慢了冷啟動速度扛芽,那就可以放到非UI線程中處理骂蓖,或者延后處理。

  • Bottom Up:一種平鋪的模式川尖,
image.png

這個模式個人用的不多登下,羅列的函數(shù)太多,沒有層次,可能單獨看排名靠前的幾個有些收益被芳。

依賴profiler基本能定位哪些函數(shù)導致了冷啟動速度慢缰贝,但是這些函數(shù)可能并非自己耗時嚴重,也許是會因為調(diào)度或者鎖的原因?qū)е侣媳簦@個時候perfetto/systrace會提供更多幫助剩晴。

perfetto/systrace:大局與調(diào)度

perfetto地址及使用文檔

perfetto/systrace是官方提供另一種性能分析工具,其中perfetto可以看做是systrace的升級版篓冲。相比MethodTracing代碼插樁李破,無法具體到每個方法宠哄,但可以提供全局性能概覽,可以更快定位問題范圍,而且perfetto/systrace在全局任務調(diào)度泳赋、系統(tǒng)調(diào)用上更具優(yōu)勢川无,MethodTracing多少對于性能有些影響,而perfetto/systrace借助系統(tǒng)本身lOG承粤,可以降低自身帶來的影響暴区,用perfetto看一下冷啟動的流程,如下:

image.png

如圖辛臊,首先你就能直觀的看到那些階段的耗時比較嚴重仙粱,然后定向分析即可,將時間段收縮彻舰,放大觀察:

image.png

可以直觀看出Activity啟動時藍色標記的資源解析耗時過長伐割,定向排查后發(fā)現(xiàn)圖大

Name    res/BKC.xml
Category    null
Start time  1s 309ms 568us 459ns
Duration    42ms 35us 682ns
Slice ID    2465

適當將圖縮小,降低加載成本刃唤,經(jīng)過優(yōu)化縮短到8ms

Name    res/BKC.xml
Category    null
Start time  964ms 602us 749ns
Duration    8ms 260us 261ns
Slice ID    11851
type    internal_slice

再比如隔心,對于有些階段,UI線程莫名的睡眠尚胞,其實可以比較方便的查看是什么因素導致的硬霍,如下:

image.png

如上所示,在當前階段笼裳,UI線程因為沒有獲取鎖進入了睡眠唯卖,之后,被另一個線程喚起了躬柬,這就可以排查到底是哪個地方有問題拜轨,是否可以避免,大概的使用方式就是如此楔脯。

對于整體冷啟動優(yōu)化效果:用perfetto看比較直接

優(yōu)化前:1261ms

image.png

優(yōu)化后:439ms

image.png

所用的優(yōu)化除了上面的措施還有部分如下措施等:

  • 延遲非必要receiver的注冊
  • 閃屏廣告Layout布局按需加載
  • 鎖優(yōu)化撩轰,進程線程間阻塞優(yōu)化

所用的優(yōu)化除了上面的措施還有部分如下措施等:核心原則 UI線程不做耗時操作

  • 延遲非必要receiver的注冊
  • 閃屏廣告Layout布局按需加載
  • 鎖優(yōu)化,進程線程間阻塞優(yōu)化

總結(jié)

BUG是必然的,優(yōu)化是持久的堪嫂,如何用好工具是關(guān)鍵的偎箫。

作者:看書的小蝸牛
原文鏈接: APP冷啟動優(yōu)化:如何使用好工具【Perfetto\ systrace \MethodTracing】

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市皆串,隨后出現(xiàn)的幾起案子淹办,更是在濱河造成了極大的恐慌,老刑警劉巖恶复,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件怜森,死亡現(xiàn)場離奇詭異,居然都是意外死亡谤牡,警方通過查閱死者的電腦和手機副硅,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來翅萤,“玉大人恐疲,你說我怎么就攤上這事√酌矗” “怎么了培己?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長胚泌。 經(jīng)常有香客問我省咨,道長,這世上最難降的妖魔是什么玷室? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任零蓉,我火速辦了婚禮,結(jié)果婚禮上阵苇,老公的妹妹穿的比我還像新娘壁公。我一直安慰自己,他們只是感情好绅项,可當我...
    茶點故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布紊册。 她就那樣靜靜地躺著,像睡著了一般快耿。 火紅的嫁衣襯著肌膚如雪囊陡。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天掀亥,我揣著相機與錄音撞反,去河邊找鬼。 笑死搪花,一個胖子當著我的面吹牛遏片,可吹牛的內(nèi)容都是我干的嘹害。 我是一名探鬼主播,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼吮便,長吁一口氣:“原來是場噩夢啊……” “哼笔呀!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起髓需,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤许师,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后僚匆,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體微渠,經(jīng)...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年咧擂,在試婚紗的時候發(fā)現(xiàn)自己被綠了逞盆。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,059評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡屋确,死狀恐怖纳击,靈堂內(nèi)的尸體忽然破棺而出续扔,到底是詐尸還是另有隱情攻臀,我是刑警寧澤,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布纱昧,位于F島的核電站刨啸,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏识脆。R本人自食惡果不足惜设联,卻給世界環(huán)境...
    茶點故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望灼捂。 院中可真熱鬧离例,春花似錦、人聲如沸悉稠。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽的猛。三九已至耀盗,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間卦尊,已是汗流浹背叛拷。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留岂却,地道東北人忿薇。 一個月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓裙椭,卻偏偏與公主長得像,于是被迫代替她去往敵國和親署浩。 傳聞我的和親對象是個殘疾皇子骇陈,可洞房花燭夜當晚...
    茶點故事閱讀 42,792評論 2 345