文章來(lái)源:
http://wuxiaolong.me/2017/03/26/Rendering/
http://m.itdecent.cn/p/989ce9eb7af8
hz:每秒刷新的次數(shù)
著名的“16ms”原則:
我們通常都會(huì)提到60fps(Frame Per Second)與16ms,可是知道為何會(huì)是以程序是否達(dá)到60fps來(lái)作為App性能的衡量標(biāo)準(zhǔn)嗎?
60fps:人眼與大腦之間的協(xié)作無(wú)法感知超過(guò)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í)候就無(wú)法進(jìn)行正常渲染,這樣就發(fā)生了丟幀現(xiàn)象。那么用戶在32ms內(nèi)看到的會(huì)是同一幀畫面。

Overdraw(過(guò)度繪制)是指系統(tǒng)在單個(gè)渲染幀中多次繪制屏幕上的像素。例如,如果我們有一堆堆疊的UI卡,不可見的UI也在做繪制的操作,這樣會(huì)浪費(fèi)大量的CPU和GPU資源。
渲染操作通常依賴于兩個(gè)核心組件:CPU與GPU。CPU負(fù)責(zé)包括Measure,Layout,Record,Execute的計(jì)算操作,GPU負(fù)責(zé)Rasterization(柵格化)操作
如何檢測(cè)?
-
Show GPU Overdraw
設(shè)置 -> 開發(fā)者選項(xiàng) -> 調(diào)試GPU過(guò)度繪制 ->顯示過(guò)度繪制區(qū)域。
對(duì)比一張Overdraw的參考圖,分別有藍(lán)色,淡綠,淡紅,深紅代表了4種不同程度的Overdraw情況:
image.png
藍(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又分別有自己的背景。僅僅是通過(guò)移除非必須的背景圖片,這就能夠減少大量的紅色Overdraw區(qū)域,增加藍(lán)色區(qū)域的占比。這一措施能夠顯著提升程序性能。
- Profile GPU Rendering
打開Profile GPU Rendering,顯示每幀畫面所需要渲染的時(shí)間。
設(shè)置 -> 開發(fā)者選項(xiàng) -> GPU呈現(xiàn)模式分析 -> 在屏幕上顯示為條形圖

界面上會(huì)滾動(dòng)顯示垂直的柱狀圖來(lái)表示每幀畫面所需要渲染的時(shí)間,柱狀圖越高表示花費(fèi)的渲染時(shí)間越長(zhǎng)。中間有一根綠色的橫線,代表16ms,我們需要確保每一幀花費(fèi)的總時(shí)間都低于這條橫線,這樣才能夠避免出現(xiàn)卡頓的問題。
-
Hierarchy Viewer
用Hierarchy Viewer工具檢查Activity中的布局是否過(guò)于復(fù)雜
Tools -> Android -> Android Device Monitor。
image.png
image.png

其實(shí)中帶有紅色或黃色的點(diǎn)代表速度較慢的View。
優(yōu)化方案:
- 刪除布局中不必要的背景屬性
- 盡量減少布局的層級(jí)
來(lái)源:https://blog.csdn.net/xyz_lmn/article/details/14524567
減少視圖層級(jí)<merge />
<merge/>標(biāo)簽在UI的結(jié)構(gòu)優(yōu)化中起著非常重要的作用,它可以刪減多余的層級(jí),優(yōu)化UI。<merge/>多用于替換FrameLayout或者當(dāng)一個(gè)布局包含另一個(gè)時(shí),<merge/>標(biāo)簽消除視圖層次結(jié)構(gòu)中多余的視圖組。例如你的主布局文件是垂直布局,引入了一個(gè)垂直布局的include,這是如果include布局使用的LinearLayout就沒意義了,使用的話反而減慢你的UI表現(xiàn)。這時(shí)可以使用<merge/>標(biāo)簽優(yōu)化。
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<Button
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/add"/>
<Button
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/delete"/>
</merge>
現(xiàn)在,當(dāng)你添加該布局文件時(shí)(使用<include />標(biāo)簽),系統(tǒng)忽略<merge />節(jié)點(diǎn)并且直接添加兩個(gè)Button。
- 需要時(shí)使用<ViewStub />
<ViewStub />標(biāo)簽最大的優(yōu)點(diǎn)是當(dāng)你需要時(shí)才會(huì)加載,使用他并不會(huì)影響UI初始化時(shí)的性能。各種不常用的布局想進(jìn)度條、顯示錯(cuò)誤消息等可以使用<ViewStub />標(biāo)簽,以減少內(nèi)存使用量,加快渲染速度。<ViewStub />是一個(gè)不可見的,大小為0的View。<ViewStub />標(biāo)簽使用如下:
<ViewStub
android:id="@+id/stub_import"
android:inflatedId="@+id/panel_import"
android:layout="@layout/progress_overlay"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom" />
當(dāng)你想加載布局時(shí),可以使用下面其中一種方法:
((ViewStub) findViewById(R.id.stub_import)).setVisibility(View.VISIBLE);
// or
View importPanel = ((ViewStub) findViewById(R.id.stub_import)).inflate();
當(dāng)調(diào)用inflate()函數(shù)的時(shí)候,ViewStub被引用的資源替代,并且返回引用的view。 這樣程序可以直接得到引用的view而不用再次調(diào)用函數(shù)findViewById()來(lái)查找了。
注:ViewStub目前有個(gè)缺陷就是還不支持 <merge /> 標(biāo)簽。


