這次我們依舊來(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)行

模擬卡頓

這里解釋一下,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)行的圖表
通過(guò)對(duì)比,我們可以看到上面二者之間的差別。流暢運(yùn)行的圖表中,每一幀的繪制很均勻。差不多16.6ms一幀,也就是1000毫秒除以60幀,得到的16.6ms一幀。而模擬卡頓的圖表中,每一幀的繪制則不均勻,有的長(zhǎng)達(dá)將近200ms。但由于是我們自身模擬的結(jié)果,并非實(shí)際卡頓,所以圖表中均為綠色的顯示。下面我們來(lái)看一個(gè)真實(shí)的案例:
真實(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í)間。
此外,還有一堆瑣碎的小片段,我們進(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ā)生卡頓的則是紅色的。
然后我們使用鍵盤(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ì)你有幫助。