如果你覺得應(yīng)用卡頓、不夠流暢蛤铜,不用懷疑,很大原因是沒有在16ms完成你的工作。
著名的“16ms”原則:
我們通常都會(huì)提到60fps(Frame Per Second)與16ms埋涧,可是知道為何會(huì)是以程序是否達(dá)到60fps來作為App性能的衡量標(biāo)準(zhǔn)嗎?
60fps:人眼與大腦之間的協(xié)作無法感知超過60fps的畫面更新奇瘦。
*16ms:因?yàn)锳ndroid設(shè)定的刷新率是60fps棘催,也就是每秒60幀,即16ms=1000/60Hz
Android系統(tǒng)每隔16ms會(huì)發(fā)出VSYNC信號(hào)重繪我們的界面耳标。
就像這樣:
如果你的某個(gè)操作花費(fèi)時(shí)間是24ms醇坝,系統(tǒng)在得到VSYNC信號(hào)的時(shí)候就無法進(jìn)行正常渲染,這樣就發(fā)生了丟幀現(xiàn)象次坡。那么用戶在32ms內(nèi)看到的會(huì)是同一幀畫面呼猪。
關(guān)于VSYNC
為了理解App是如何進(jìn)行渲染的,我們必須了解手機(jī)硬件是如何工作砸琅,那么就必須理解什么是VSYNC宋距。
在講解VSYNC之前,我們需要了解兩個(gè)相關(guān)的概念:
Refresh Rate:代表了屏幕在一秒內(nèi)刷新屏幕的次數(shù)明棍,這取決于硬件的固定參數(shù)乡革,例如60Hz。
Frame Rate:代表了GPU在一秒內(nèi)繪制操作的幀數(shù)摊腋,例如30fps沸版,60fps。
GPU會(huì)獲取圖形數(shù)據(jù)進(jìn)行渲染兴蒸,然后硬件負(fù)責(zé)把渲染后的內(nèi)容呈現(xiàn)到屏幕上视粮,他們兩者不停的進(jìn)行協(xié)作。
不幸的是橙凳,刷新頻率和幀率并不是總能夠保持相同的節(jié)奏蕾殴。如果發(fā)生幀率與刷新頻率不一致的情況笑撞,就會(huì)容易出現(xiàn)Tearing的現(xiàn)象(畫面上下兩部分顯示內(nèi)容發(fā)生斷裂,來自不同的兩幀數(shù)據(jù)發(fā)生重疊)钓觉。
雙緩沖機(jī)制
其實(shí)上面說的就是Android的雙緩沖機(jī)制茴肥,而雙緩沖技術(shù)一直貫穿這個(gè)Android系統(tǒng)。因?yàn)閷?shí)際上幀的數(shù)據(jù)就是保存在兩個(gè)緩沖區(qū)中荡灾,A緩沖用來顯示當(dāng)前幀瓤狐,那么B緩沖就用來緩存下一幀的數(shù)據(jù),這樣就可以做到一邊顯示一邊處理下一幀的數(shù)據(jù)批幌。
前面的幀用序號(hào)表示础锐,但實(shí)際上幀數(shù)據(jù)只保存在A、B兩個(gè)緩沖區(qū)中荧缘。當(dāng)前幀顯示緩沖A皆警,Android系統(tǒng)一旦發(fā)出VSYN信號(hào)時(shí),就會(huì)在緩沖B中構(gòu)建新的幀截粗。當(dāng)完成后(這里的完成指的是屏幕已經(jīng)在緩沖B中拿到新一幀的數(shù)據(jù)信姓,完成繪制),緩沖A的數(shù)據(jù)就會(huì)被清空桐愉,繼續(xù)進(jìn)行下一幀的繪制财破,注意,此時(shí)緩沖B的數(shù)據(jù)是不會(huì)被清空的从诲,因?yàn)楫?dāng)前顯示的是緩沖B中幀畫面左痢,清空的只是緩沖A的數(shù)據(jù)。
??這樣看起來貌似沒什么問題系洛,一切都是我們的掌控中俊性。但是,由于某些原因描扯,比如我們應(yīng)用代碼上處理不夠好定页,又或者用戶手機(jī)后臺(tái)打開了很多應(yīng)用,又在聽歌又在下載視頻什么的绽诚,CPU一時(shí)間被占用了典徊,導(dǎo)致下一幀繪制的時(shí)間超過了16ms,那么問題就來了恩够,這時(shí)候用戶就不爽了卒落,因?yàn)橛脩艉苊黠@感知到了卡頓的出現(xiàn),也就是所謂的丟幀情況蜂桶。如下圖所示:
很好儡毕,下面我們來認(rèn)真分析一下為什么會(huì)出現(xiàn)丟幀的情況:
Step1. 當(dāng)Display顯示第0幀數(shù)據(jù),此時(shí)CPU和GPU已經(jīng)開始渲染第1幀畫面扑媚,并將數(shù)據(jù)緩存在緩沖B中腰湾;
Step2. 但是由于某些原因雷恃,就好像上面說的,CPU資源一時(shí)間被占用费坊,導(dǎo)致系統(tǒng)處理該幀數(shù)據(jù)耗時(shí)過長或者未能及時(shí)處理該幀數(shù)據(jù)倒槐;
Step3. 當(dāng)VSYNC信號(hào)來時(shí),display向B緩沖要數(shù)據(jù)葵萎,這下悲催了导犹,因?yàn)榫彌_B的數(shù)據(jù)還沒準(zhǔn)備好,B緩沖區(qū)這時(shí)候是被鎖定的羡忘,display無可奈何,只能繼續(xù)顯示之前緩沖A的那一幀磕昼,此時(shí)緩沖A的數(shù)據(jù)也不能被清空和交換數(shù)據(jù)卷雕。這種情況被Android開發(fā)組命名為“Jank”,就是所謂的“丟幀”票从,也被稱作“廢幀”漫雕;
Step4. 當(dāng)?shù)?幀數(shù)據(jù)(即緩沖B數(shù)據(jù))準(zhǔn)備完成后,它并不會(huì)馬上被顯示峰鄙,而是要等待下一個(gè)VSYNC浸间,Display刷新后,這時(shí)用戶才看到畫面的更新吟榴,中間這段時(shí)間的時(shí)間就白白被浪費(fèi)掉了魁蒜。
從上面的分析可以知道,因?yàn)榫彌_B的超時(shí)吩翻,掉了鏈子兜看,導(dǎo)致出現(xiàn)了丟幀的情況。因?yàn)橐徊降难舆t狭瞎,也很有可能導(dǎo)致后面的處理延遲细移,很可能造成一步慢步步慢啊。
三倍緩沖機(jī)制
出現(xiàn)上面這種情況怎么辦熊锭,在Android系統(tǒng)里給出了這樣的解決辦法就是:再加入一個(gè)緩沖弧轧。這樣就出現(xiàn)了三個(gè)緩沖,顧名思義碗殷,這里說的就是三倍緩沖精绎。好,看下圖:
當(dāng)出現(xiàn)B緩沖超時(shí),屏幕顯示的還是緩沖A中的那一幀亿扁,因?yàn)榇藭r(shí)緩沖A的數(shù)據(jù)還在使用捺典,不能及時(shí)被交換,所以在下一次VSYNC信號(hào)來之前這段時(shí)間無任何作為从祝,時(shí)間就會(huì)白白被浪費(fèi)襟己。為了避免這種時(shí)間浪費(fèi)引谜,在三倍緩沖機(jī)制中,系統(tǒng)這個(gè)時(shí)候會(huì)創(chuàng)建一個(gè)緩沖C擎浴,用來緩沖下一幀的數(shù)據(jù)员咽。如上圖所示,顯示完緩沖B中那一幀后贮预,下一幀就是顯示緩沖C中的了贝室。這樣雖然還是不能避免會(huì)出現(xiàn)卡頓的情況,但是Android系統(tǒng)還是盡力去彌補(bǔ)這種缺陷仿吞,最終盡可能給用平滑的動(dòng)效體驗(yàn)滑频。
Overdraw解決方法
Overdraw(過度繪制)描述的是屏幕上的某個(gè)像素在同一幀的時(shí)間內(nèi)被繪制了多次。在多層次的UI結(jié)構(gòu)里面唤冈,如果不可見的UI也在做繪制的操作峡迷,這就會(huì)導(dǎo)致某些像素區(qū)域被繪制了多次。這就浪費(fèi)大量的CPU以及GPU資源你虹。
1. 使用HierarchyViewer來查找Activity中的布局是否過于復(fù)雜
2. 打開Show GPU Overdraw的選項(xiàng)绘搞,觀察UI上的Overdraw情況
藍(lán)色,淡綠傅物,淡紅夯辖,深紅代表了4種不同程度的Overdraw情況,我們的目標(biāo)就是盡量減少紅色Overdraw董饰,看到更多的藍(lán)色區(qū)域蒿褂。
Overdraw有時(shí)候是因?yàn)槟愕腢I布局存在大量重疊的部分,還有的時(shí)候是因?yàn)榉潜仨毜闹丿B背景尖阔。例如某個(gè)Activity有一個(gè)背景贮缅,然后里面的Layout又有自己的背景,同時(shí)子View又分別有自己的背景介却。僅僅是通過移除非必須的背景圖片谴供,這就能夠減少大量的紅色Overdraw區(qū)域,增加藍(lán)色區(qū)域的占比齿坷。這一措施能夠顯著提升程序性能桂肌。
3. 使用TraceView來觀察CPU的執(zhí)行情況,更加快捷的找到性能瓶頸
4. Profile GPU Rendering永淌,選中On screen as bars選項(xiàng)
選擇了這樣以后崎场,我們可以在手機(jī)畫面上看到豐富的GPU繪制圖形信息,分別關(guān)于StatusBar遂蛀,NavBar谭跨,激活的程序Activity區(qū)域的GPU Rending信息。
中間有一根綠色的橫線,代表16ms螃宙,我們需要確保每一幀花費(fèi)的總時(shí)間都低于這條橫線蛮瞄,這樣才能夠避免出現(xiàn)卡頓的問題。
參考資料:
http://www.csdn.net/article/2015-01-20/2823621-android-performance-patterns/1
http://www.reibang.com/p/02800806356c
https://www.youtube.com/watch?v=HXQhu6qfTVU&list=PLWz5rJ2EKKc9CBxr3BVjPTPoDPLdPIFCE&index=58
http://www.reibang.com/p/1fb065c806e6