抓出卡頓元兇,從分析掉幀開(kāi)始

這次我們依舊來(lái)談?wù)動(dòng)嘘P(guān)性能優(yōu)化的話題,這次我們會(huì)用到Google給我們提供的分析工具——Systrace。如果你還不了解這個(gè)工具,最好先了解一下。Google 官方文檔:
https://developer.android.com/studio/command-line/systrace
我們還會(huì)用到一個(gè)Demo,用來(lái)對(duì)比卡頓和不卡頓的狀況。

問(wèn)題重現(xiàn)

Demo運(yùn)行起來(lái)會(huì)是這樣的:
流暢運(yùn)行

流暢運(yùn)行的錄屏

模擬卡頓

模擬卡頓的錄屏

這里解釋一下,GIF動(dòng)畫(huà)表現(xiàn)得不是很完善,流暢運(yùn)行的效果其實(shí)是每秒60幀,實(shí)際運(yùn)行效果非常順暢。模擬卡頓的效果在每秒60幀的基礎(chǔ)上加了隨機(jī)時(shí)長(zhǎng)的線程sleep時(shí)間。具體實(shí)驗(yàn)代碼片如下所示:

流暢運(yùn)行的代碼片

        threadRun = true;
        pbCurrent = 0;
        demoPb.setProgress(pbCurrent);
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (threadRun) {
                    try {
                        Thread.sleep(1000 / 60);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    if (pbCurrent > PB_MAX) {
                        pbCurrent = 0;
                    } else {
                        pbCurrent++;
                    }
                    Message msg = new Message();
                    msg.what = UPDATE_HANDLER_KEY;
                    mUiHandler.sendMessage(msg);
                }
            }
        }).start();

模擬卡頓的代碼片

        thread2Run = true;
        pbCurrent = 0;
        demoPb.setProgress(pbCurrent);
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (thread2Run) {
                    try {
                        Thread.sleep(1000 / 60);
                        Thread.sleep(new Random().nextInt(200));
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    if (pbCurrent > PB_MAX) {
                        pbCurrent = 0;
                    } else {
                        pbCurrent++;
                    }
                    Message msg = new Message();
                    msg.what = UPDATE_HANDLER_KEY;
                    mUiHandler.sendMessage(msg);
                }
            }
        }).start();

更新UI部分代碼片

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
                case UPDATE_HANDLER_KEY:
                    demoPb.setProgress(pbCurrent);
                    break;
            }
        }

兩個(gè)按鈕分別對(duì)應(yīng)上述兩個(gè)線程的使能,另外請(qǐng)注意:我們只是模擬卡頓,并非真的發(fā)生了卡頓。因此,在Systrace的圖表中,沒(méi)有出現(xiàn)紅色或橙色的告警。
分別對(duì)上述兩種情況取Systrace圖表,得到如下結(jié)果:

流暢運(yùn)行的圖表

流暢運(yùn)行的Systrace圖表

模擬卡頓運(yùn)行的圖表
模擬卡頓運(yùn)行的圖表

通過(guò)對(duì)比,我們可以看到上面二者之間的差別。流暢運(yùn)行的圖表中,每一幀的繪制很均勻。差不多16.6ms一幀,也就是1000毫秒除以60幀,得到的16.6ms一幀。而模擬卡頓的圖表中,每一幀的繪制則不均勻,有的長(zhǎng)達(dá)將近200ms。但由于是我們自身模擬的結(jié)果,并非實(shí)際卡頓,所以圖表中均為綠色的顯示。下面我們來(lái)看一個(gè)真實(shí)的案例:

真實(shí)案例

卡頓發(fā)生的真實(shí)案例

上圖中,一幀本來(lái)應(yīng)該是16ms完成的,然而卻花費(fèi)了近60ms,用1000ms/60ms,我們得到近似16幀。而16幀的幀率已經(jīng)是肉眼可見(jiàn)的卡頓了。

揪出兇手

我們聚焦到上面真實(shí)的案例,放大看發(fā)生卡頓的位置:

放大-第一步

我們發(fā)現(xiàn),Record View 的draw()方法花費(fèi)了一些時(shí)間。
不正常的draw()方法

此外,還有一堆瑣碎的小片段,我們進(jìn)一步放大觀察,會(huì)發(fā)現(xiàn):
放大-第二步

這里居然還加載了一堆貼圖。
至此,我們就抓到了導(dǎo)致掉幀的“元兇”,下一步就是結(jié)合源代碼進(jìn)行優(yōu)化了。

一些疑問(wèn)和技巧

為什么16ms一幀?
16ms是1000ms/60幀得到的結(jié)果,60幀對(duì)于人眼而言已經(jīng)是很流暢的體驗(yàn)了。而最低的限度是33ms一幀,也就是1000ms/30幀得到的結(jié)果。如果時(shí)間再長(zhǎng)一點(diǎn)的話,就有可能發(fā)生人眼可見(jiàn)的卡頓了。
延伸一點(diǎn),也就是說(shuō),如果嚴(yán)格要求60幀,但是中間掉了1幀,就相當(dāng)于33ms畫(huà)一幀,此時(shí),雖然掉幀,但是人眼還是可接受的。

如何快速定位卡頓位置
首先是確保發(fā)生了卡頓。一般而言,沒(méi)有發(fā)生卡頓的圖表,網(wǎng)頁(yè)的圖表會(huì)是綠色的,發(fā)生卡頓的則是紅色的。

網(wǎng)頁(yè)Logo

然后我們使用鍵盤(pán)+鼠標(biāo)的組合來(lái)找位置,鍵盤(pán)的快捷鍵對(duì)應(yīng)W、S、A、D。AD相當(dāng)于拖拽時(shí)間滑塊,WS相當(dāng)于縮放。
最后我們用鼠標(biāo)來(lái)選取相應(yīng)的時(shí)間范圍即可。

今天的分享到此,希望對(duì)你有幫助。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1.23這個(gè)日子 只有我們知道 還真蠻好記的,已經(jīng)60多天了 6月份,你就要走了 一去就是四五個(gè)月 我沒(méi)能力 我不...
    卷卷醬cony閱讀 284評(píng)論 0 0
  • 今年剛一開(kāi)始,我感覺(jué)到周?chē)姆諊ツ瓴惶粯?。大家都在朝著自己的方向前行,而無(wú)需顧慮周?chē)沫h(huán)境,而我也很幸運(yùn)的加...
    夏野閱讀 220評(píng)論 0 0
  • 周末是稀缺品,望眼欲穿才有兩日,轉(zhuǎn)眼又消失不見(jiàn)。周末是用五日的辛勞換來(lái)的,也是五日盼來(lái)的,來(lái)之不易。有些人周末也在...
    七月up閱讀 1,119評(píng)論 0 8
  • 下班后,不急不慌一個(gè)人往住處走,望去,泛黃的路燈映襯到被雨水打濕的樹(shù)葉上,昏暗的光線穿透到濕漉漉的地上!身旁無(wú)數(shù)個(gè)...
    鼓子雨閱讀 368評(píng)論 0 1

友情鏈接更多精彩內(nèi)容