App啟動(dòng)優(yōu)化(二)啟動(dòng)時(shí)間測(cè)量

如需轉(zhuǎn)載請(qǐng)?jiān)u論或簡(jiǎn)信傅是,并注明出處瑞信,未經(jīng)允許不得轉(zhuǎn)載

系列文章

目錄

前言

在進(jìn)行啟動(dòng)優(yōu)化之前拄丰,我們應(yīng)該先了解如何對(duì)啟動(dòng)的時(shí)間進(jìn)行測(cè)量,這樣就能更加準(zhǔn)確的衡量我們優(yōu)化的成果

adb命令測(cè)量

adb shell am start -W packagename/首屏Activity

Tips:這個(gè)命令可以打開(kāi)Launcher Activity擦剑,如果在打開(kāi)別的Activity的時(shí)候遇到問(wèn)題蜡坊,可以在清單文件中配置Activity的exported = true

啟動(dòng)時(shí)間.png
  • ThisTime:一般和TotalTime時(shí)間一樣,除非在應(yīng)用啟動(dòng)時(shí)開(kāi)了一個(gè)透明的Activity預(yù)先處理一些事翅阵,再顯示出Activity歪玲,這樣將比TotalTime小
  • TotalTime:應(yīng)用的啟動(dòng)時(shí)間,包括創(chuàng)建進(jìn)程+Application初始化+Activity初始化到界面顯示
  • WaitTime:一般比TotalTime大點(diǎn)掷匠,包括系統(tǒng)響應(yīng)的耗時(shí)

這三個(gè)時(shí)間不好理解滥崩,我們可以把整個(gè)過(guò)程分解,如下所示:

  1. 前一個(gè)應(yīng)用的activityonPause()

  2. 系統(tǒng)調(diào)用AMS耗時(shí)

  3. 第一個(gè)activity啟動(dòng)耗時(shí)

  4. 第一個(gè)activityonPause()耗時(shí)

  5. 第二個(gè)activity啟動(dòng)耗時(shí)

ThisTime表示5

TotalTime表示3讹语、4钙皮、5總共的耗時(shí)

WaitTime則表示所有的操作耗時(shí),即1顽决、2短条、3、4擎值、5所有的耗時(shí)

開(kāi)發(fā)者一般只要關(guān)心 TotalTime 即可慌烧,這個(gè)時(shí)間才是自己應(yīng)用真正啟動(dòng)的耗時(shí)

使用adb命令測(cè)量啟動(dòng)時(shí)間的特點(diǎn):

  • 應(yīng)用的啟動(dòng)過(guò)程往往不只一個(gè)Activity,有可能是先進(jìn)入一個(gè)啟動(dòng)頁(yè)鸠儿,然后再?gòu)膯?dòng)頁(yè)打開(kāi)真正的首頁(yè)屹蚊。某些情況下還有可能中間經(jīng)過(guò)更多的Activity,這個(gè)時(shí)候需要將多個(gè)Activity的時(shí)間加起來(lái)
  • 將多個(gè)Activity啟動(dòng)時(shí)間加起來(lái)并不完全等于用戶感知的啟動(dòng)時(shí)間进每。例如在啟動(dòng)頁(yè)可能是先等待某些初始化完成或者某些動(dòng)畫(huà)播放完畢后再進(jìn)入首頁(yè)汹粤。使用命令行統(tǒng)計(jì)的方式只是計(jì)算了Activity的啟動(dòng)以及初始化時(shí)間,并不能體現(xiàn)這種等待任務(wù)的時(shí)間
  • 這種方式只能在線下使用田晚,不能再線上使用

手動(dòng)打點(diǎn)測(cè)量(推薦)

手動(dòng)打點(diǎn)就是啟動(dòng)時(shí)埋點(diǎn)嘱兼,啟動(dòng)結(jié)束埋點(diǎn),計(jì)算二者差值贤徒。這種方式比較精確芹壕,可以帶到線上測(cè)量用戶的啟動(dòng)時(shí)間,所以推薦使用這種方式

那么什么時(shí)候算啟動(dòng)開(kāi)始接奈,什么時(shí)候算啟動(dòng)結(jié)束呢踢涌?

我們先寫(xiě)一個(gè)測(cè)量啟動(dòng)時(shí)間的工具類

public class LaunchTimer {

    private static long sTime;

    public static void startRecord() {
        sTime = System.currentTimeMillis();
    }

    public static void endRecord() {
        endRecord("");
    }

    public static void endRecord(String msg) {
        long cost = System.currentTimeMillis() - sTime;
        LogUtils.i(msg + " cost " + cost + "ms");
    }

}

啟動(dòng)時(shí)間點(diǎn)

啟動(dòng)時(shí)間點(diǎn)比較好記錄,我們一般會(huì)在Application attachBaseContext()開(kāi)始記錄序宦,因?yàn)樵谶@之前 Context 還沒(méi)有初始化睁壁,一般也干不了什么事情,當(dāng)然這個(gè)是要視具體情況來(lái)定,其實(shí)只要保證在 App 的具體業(yè)務(wù)邏輯開(kāi)始執(zhí)行之前記錄起始時(shí)間點(diǎn)即可

@Override
protected void attachBaseContext(Context base) {
    super.attachBaseContext(base);
    LauncherTimer.startRecord();
}

結(jié)束時(shí)間點(diǎn)

我們究竟要在哪里進(jìn)行啟動(dòng)結(jié)束的記錄呢潘明?

一個(gè) Activity 走完onCreate onStart onResume 這幾個(gè)生命周期之后行剂,完成了應(yīng)用自身的一些配置,比如 Activity 主題設(shè)置 window 屬性的設(shè)置 View 樹(shù)的建立,但是其實(shí)后面還需要各個(gè) View 執(zhí)行 measure layout draw等钳降。Activity的首幀時(shí)間實(shí)際上是 Activity.onWindowFocusChanged厚宰,下面是它的注釋:

/**
*Called when the current {@link Window} of the activity gains or loses
* focus.  This is the best indicator of whether this activity is visible
* to the user.  The default implementation clears the key tracking
* state, so should always be called.
...
*/

我們可以在onWindowFocusChanged中進(jìn)行打點(diǎn),用于統(tǒng)計(jì)應(yīng)用從啟動(dòng)到首幀繪制的時(shí)間

private boolean mHasRecord;

@Override
public void onWindowFocusChanged(boolean hasFocus) {
    super.onWindowFocusChanged(hasFocus);
    if (!mHasRecord) {
        mHasRecord = true;
        LaunchTimer.endRecord("onWindowFocusChanged");
    }
}

但是實(shí)際上對(duì)于用戶來(lái)說(shuō)牲阁,onWindowFocusChanged()所統(tǒng)計(jì)出的首幀時(shí)間固阁,并不能夠代表這個(gè)頁(yè)面真正被展現(xiàn)出來(lái)了,為什么呢城菊?舉個(gè)例子备燃,假設(shè)我們的APP首頁(yè)是一個(gè)列表,這個(gè)列表的數(shù)據(jù)一般是從網(wǎng)絡(luò)上獲取的凌唬,那么用戶看到的”首幀“并齐,往往就是網(wǎng)絡(luò)請(qǐng)求成功后列表的第一個(gè)Item展示出來(lái)的時(shí)間。我們做性能優(yōu)化主要是為了能更好的改善用戶的體驗(yàn)客税,而不僅僅是為了數(shù)據(jù)上面的好看况褪,所以在做啟動(dòng)優(yōu)化的時(shí)候,我們不僅僅要考慮慮onWindowFocusChanged()前的過(guò)程更耻,也要考慮onWindowFocusChanged()后一直到用戶真正看到頁(yè)面的過(guò)程

正確的解答方式是我們要等真實(shí)的數(shù)據(jù)展示出來(lái)测垛,這樣才算啟動(dòng)結(jié)束了,這里我們采用Feed第一條數(shù)據(jù)的展示作為啟動(dòng)結(jié)束

public class HomeAdapter extends RecyclerView.Adapter {
    ....
    //只需要記錄一次
    private boolean mHasRecorded;

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        ......
        return viewholder;
    }

    @Override
    public void onBindViewHolder(final RecyclerView.ViewHolder holder, int position) {
        if (position == 0 && !mHasRecorded) {
            mHasRecorded = true;
            holder.itemView.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
                @Override
                public boolean onPreDraw() {
                    //注銷監(jiān)聽(tīng)
                    holder.itemView.getViewTreeObserver().removeOnPreDrawListener(this);
                    //啟動(dòng)結(jié)束
                    LauncherTimer.endRecord("feedShow");
                    return true;
                }
            });
        }
        .....
    }

    @Override
    public int getItemCount() {
        return datas.size();
    }
}

通過(guò)這種方式測(cè)量出來(lái)的啟動(dòng)時(shí)間和通過(guò)onWindowFocusChanged()測(cè)量的啟動(dòng)時(shí)間一般差距是比較大的秧均,大家也可以自己實(shí)踐一下

Tips:addOnDrawListener要求API 16食侮,所以這里使用了addOnPreDrawListener

總結(jié)

本文我們介紹了啟動(dòng)優(yōu)化時(shí)間的測(cè)量,有adb和手動(dòng)打點(diǎn)兩種方式目胡,adb方式的缺點(diǎn)是測(cè)量結(jié)果和實(shí)際表現(xiàn)有偏差和不能帶到線上使用锯七,所以這里我推薦使用手動(dòng)打點(diǎn)的方式進(jìn)行啟動(dòng)時(shí)間測(cè)量,因?yàn)槲覀冏鰡?dòng)優(yōu)化要從真實(shí)用戶體驗(yàn)的角度出發(fā)誉己,而不是僅僅追求數(shù)據(jù)上的結(jié)果

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
禁止轉(zhuǎn)載眉尸,如需轉(zhuǎn)載請(qǐng)通過(guò)簡(jiǎn)信或評(píng)論聯(lián)系作者。
  • 序言:七十年代末巨双,一起剝皮案震驚了整個(gè)濱河市噪猾,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌筑累,老刑警劉巖畏妖,帶你破解...
    沈念sama閱讀 211,194評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異疼阔,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,058評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門(mén)婆廊,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)迅细,“玉大人,你說(shuō)我怎么就攤上這事淘邻∫鸬洌” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,780評(píng)論 0 346
  • 文/不壞的土叔 我叫張陵宾舅,是天一觀的道長(zhǎng)统阿。 經(jīng)常有香客問(wèn)我,道長(zhǎng)筹我,這世上最難降的妖魔是什么扶平? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,388評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮蔬蕊,結(jié)果婚禮上结澄,老公的妹妹穿的比我還像新娘。我一直安慰自己岸夯,他們只是感情好麻献,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,430評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著猜扮,像睡著了一般勉吻。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上旅赢,一...
    開(kāi)封第一講書(shū)人閱讀 49,764評(píng)論 1 290
  • 那天齿桃,我揣著相機(jī)與錄音,去河邊找鬼鲜漩。 笑死源譬,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的孕似。 我是一名探鬼主播踩娘,決...
    沈念sama閱讀 38,907評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼喉祭!你這毒婦竟也來(lái)了养渴?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,679評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤泛烙,失蹤者是張志新(化名)和其女友劉穎理卑,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體蔽氨,經(jīng)...
    沈念sama閱讀 44,122評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡藐唠,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,459評(píng)論 2 325
  • 正文 我和宋清朗相戀三年帆疟,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片宇立。...
    茶點(diǎn)故事閱讀 38,605評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡踪宠,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出妈嘹,到底是詐尸還是另有隱情柳琢,我是刑警寧澤,帶...
    沈念sama閱讀 34,270評(píng)論 4 329
  • 正文 年R本政府宣布润脸,位于F島的核電站柬脸,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏毙驯。R本人自食惡果不足惜倒堕,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,867評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望尔苦。 院中可真熱鬧涩馆,春花似錦、人聲如沸允坚。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,734評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)稠项。三九已至涯雅,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間展运,已是汗流浹背活逆。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,961評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留拗胜,地道東北人蔗候。 一個(gè)月前我還...
    沈念sama閱讀 46,297評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像埂软,于是被迫代替她去往敵國(guó)和親锈遥。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,472評(píng)論 2 348