? ? 界面是 Android 應(yīng)用中直接影響用戶體驗(yàn)最關(guān)鍵的部分。如果代碼實(shí)現(xiàn)得不好,界面容易發(fā)生卡頓且導(dǎo)致應(yīng)用占用大量內(nèi)存。
????????我司這類做 ROM 的公司更不一樣,預(yù)裝的應(yīng)用一定要非常流暢,這樣給客戶或用戶的第一感覺就是快。又卡又慢的應(yīng)用體驗(yàn),會(huì)影響客戶或用戶對(duì)產(chǎn)品的信心和評(píng)價(jià),所以不可忽視。
目錄
一. Android渲染知識(shí)
1.1 繪制原理
1.2 掉幀
1.3 為什么是60Fps?
1.4 垃圾回收
1.5 UI 線程
1.6 垂直同步
1.7 UI 繪制機(jī)制與柵格化
二. 檢測和解決
2.1 檢測維度
2.2 調(diào)試工具
2.3 如何解決
三. 界面過度繪制
3.1 過度繪制概念
3.2 追蹤過度繪制
3.3 過度繪制的根源
3.4 不合理的xml布局對(duì)繪制的影響
3.5 源碼相關(guān)
四. 渲染性能
4.1 渲染性能概念
4.2 追蹤渲染性能
4.3 渲染性能差的根源
4.4 檢測說明
4.5 UI繪制機(jī)制的補(bǔ)充說明
五. 布局邊界合理性
六. 給開發(fā)的界面優(yōu)化建議
6.1 優(yōu)化布局的結(jié)構(gòu)
6.2 優(yōu)化處理邏輯
6.3 善用 DEBUG 工具
一. Android渲染知識(shí)
1.1 繪制原理
????????Android系統(tǒng)要求每一幀都要在 16ms 內(nèi)繪制完成,平滑的完成一幀意味著任何特殊的幀需要執(zhí)行所有的渲染代碼(包括 framework 發(fā)送給 GPU 和 CPU 繪制到緩沖區(qū)的命令)都要在 16ms 內(nèi)完成,保持流暢的體驗(yàn)。這個(gè)速度允許系統(tǒng)在動(dòng)畫和輸入事件的過程中以約 60 幀每秒( 1秒 / 0.016幀每秒 = 62.5幀/秒 )的平滑幀率來渲染。
????????如果你的應(yīng)用沒有在 16ms 內(nèi)完成這一幀的繪制,假設(shè)你花了 24ms 來繪制這一幀,那么就會(huì)出現(xiàn)掉幀的情況。
????????系統(tǒng)準(zhǔn)備將新的一幀繪制到屏幕上,但是這一幀并沒有準(zhǔn)備好,所有就不會(huì)有繪制操作,畫面也就不會(huì)刷新。反饋到用戶身上,就是用戶盯著同一張圖看了 32ms 而不是 16ms ,也就是說掉幀發(fā)生了。
1.2 掉幀
????????掉幀是用戶體驗(yàn)中一個(gè)非常核心的問題。丟棄了當(dāng)前幀,并且之后不能夠延續(xù)之前的幀率,這種不連續(xù)的間隔會(huì)容易會(huì)引起用戶的注意,也就是我們常說的卡頓、不流暢。
????????引起掉幀的原因非常多,比如:
花了非常多時(shí)間重新繪制界面中的大部分東西,這樣非常浪費(fèi)CPU周期;
過度繪制嚴(yán)重,在繪制用戶看不到的對(duì)象上花費(fèi)了太多的時(shí)間;
有一大堆動(dòng)畫重復(fù)了一遍又一遍,消耗 CPU 、 GPU 資源;
頻繁的觸發(fā)垃圾回收;
1.3 為什么是60Fps?
????????Android系統(tǒng)要求每一幀都要在 16ms 內(nèi)繪制完成,那么1秒的幀率就是約 60 幀每秒( 1秒 / 0.016幀每秒 = 62.5幀/秒 ),那為什么要以 60 Fps來作為 App 性能的衡量標(biāo)準(zhǔn)呢?這是因?yàn)槿搜酆痛竽X之間的協(xié)作無法感知到超過 60 Fps的畫面更新。
????????市面上絕大多數(shù)Android設(shè)備的屏幕刷新頻率是 60 HZ。當(dāng)然,超過 60 Fps 是沒有意義的,人眼感知不到區(qū)別。24 Fps 是人眼能感知的連續(xù)線性的運(yùn)動(dòng),所以是電影膠圈的常用幀率,因?yàn)檫@個(gè)幀率已經(jīng)足夠支撐大部分電影畫面所要表達(dá)的內(nèi)容,同時(shí)能最大限度地減少費(fèi)用支出。但是,低于 30 Fps 是無法順暢表現(xiàn)絢麗的畫面內(nèi)容的,此時(shí)就需要用到 60 Fps 來達(dá)到想要表達(dá)的效果。了解更多Fps知識(shí)詳見「?Wiki?」。
????????應(yīng)用的界面性能目標(biāo)就是保持 60 Fps,這意味著每一幀你只有 16 ms(1秒 / 60幀率)的時(shí)間來處理所有的任務(wù)。
1.4 垃圾回收
????????垃圾回收器是一個(gè)在應(yīng)用運(yùn)行期間自動(dòng)釋放那些不再引用的內(nèi)存的機(jī)制,常稱 GC 。頻繁的 GC 也是導(dǎo)致嚴(yán)重性能問題的罪魁禍?zhǔn)字弧?/span>
????????前面提到,平滑的完成一幀意味著所有渲染代碼都必須在 16ms 內(nèi)完成。頻繁的 GC 會(huì)嚴(yán)重限制一幀時(shí)間內(nèi)的剩余時(shí)間,如果 GC 所做的工作超過了那些必須的工作,那么留給應(yīng)用平滑的幀率的時(shí)間就越少。越接近 16ms ,在垃圾回收事件觸發(fā)的時(shí)候,就越容易導(dǎo)致卡頓。
????????注意,Android4.4 引進(jìn)了新的 ART 虛擬機(jī)來取代 Dalvik 虛擬機(jī)。它們的機(jī)制大有不同,簡單而言:
Dalvik 虛擬機(jī)的 GC 是非常耗資源的,并且在正常的情況下一個(gè)硬件性能不錯(cuò)的Android設(shè)備也會(huì)很容易耗費(fèi)掉 10 – 20 ms 的時(shí)間;
ART 虛擬機(jī)的GC會(huì)動(dòng)態(tài)提升垃圾回收的效率,在 ART 中的中斷,通常在 2 – 3 ms 間。 比 Dalvik 虛擬機(jī)有很大的性能提升;
????????ART 虛擬機(jī)相對(duì)于 Dalvik 虛擬機(jī)來說的垃圾回收來說有一個(gè)很大的性能提升,但 2 – 3 ms 的回收時(shí)間對(duì)于超過16ms幀率的界限也是足夠的。因此,盡管垃圾回收在 Android 5.0 之后不再是耗資源的行為,但也是始終需要盡可能避免的,特別是在執(zhí)行動(dòng)畫的情況下,可能會(huì)導(dǎo)致一些讓用戶明顯感覺的丟幀。
????????想了解更多詳細(xì)的 ART 和 Dalvik 虛擬機(jī)垃圾回收機(jī)制,可「?戳我?」和「?我?」進(jìn)行深入了解。
1.5 UI 線程
????????UI 線程是應(yīng)用的主線程,很多的性能和卡頓問題是由于我們?cè)谥骶€程中做了大量的工作。
????????所以,所有耗資源的操作,比如 IO 操作、網(wǎng)絡(luò)操作、SQL 操作、列表刷新等,都應(yīng)該用后臺(tái)進(jìn)程去實(shí)現(xiàn),不能占用主線程,主線程是 UI 線程,是保持程序流暢的關(guān)鍵;
????????在 Android 5.0 版本里,Android 框架層引入了 “ Render Thread ” ,用于向 GPU 發(fā)送實(shí)際渲染的操作。這個(gè)線程減輕了一些 UI 線程減少的操作。但是輸入、滾動(dòng)和動(dòng)畫仍然在 UI thread,因?yàn)?Thread 必須能夠響應(yīng)操作。
1.6 垂直同步
????????垂直同步是 Android4.1 通過 Project Butter 在 UI 架構(gòu)中引入的新技術(shù),同期引入的還有 Triple Buffer 和 HWComposer 等技術(shù),都是為提高 UI 的流暢性而生。
????????舉個(gè)例子,你拍了一張照片,然后旋轉(zhuǎn)5度再拍另外一張照片,將兩照片的中間剪開并拼接在一起,得到下圖:
????????中間這部分有明顯區(qū)別的部分,等價(jià)于設(shè)備刷新率和幀速率不一致的結(jié)果。
????????一般而言, GPU 的幀速率應(yīng)高于刷新率,才不會(huì)卡頓或掉幀。如果屏幕刷新率比幀速率還快,屏幕會(huì)在兩幀中顯示同一個(gè)畫面,這種斷斷續(xù)續(xù)情況持續(xù)發(fā)生時(shí),用戶將會(huì)很明顯地感覺到動(dòng)畫的卡頓或者掉幀,然后又恢復(fù)正常,我們常稱之為閃屏、跳幀、延遲。
????????應(yīng)用應(yīng)避免這些幀率下降的情況,以確保 GPU 能在屏幕刷新之前完成數(shù)據(jù)的獲取及寫入,保證動(dòng)畫流暢。
1.7 UI 繪制機(jī)制與柵格化
????????絕大多數(shù)渲染操作都依賴兩個(gè)硬件: CPU 、 GPU 。 CPU 負(fù)責(zé) Measure 、 layout 、 Record 、 Execute 的計(jì)算操作, GPU 負(fù)責(zé)柵格化( Rasterization )操作。 非必需的視圖組件會(huì)帶來多余的 CPU 計(jì)算操作,還會(huì)占用多余的 GPU 資源。
????????柵格化( Rasterization )能將 Button 、 Shape 、 Path 、 Bitmap 等資源組件拆分到不同的像素上進(jìn)行顯示。這個(gè)操作很費(fèi)時(shí),所以引入了 GPU 來加快柵格化的操作。
????????CPU 負(fù)責(zé)把 UI 組件計(jì)算成多邊形( Polygons ),紋理( Texture ),然后交給 GPU 進(jìn)行柵格化渲染,再將處理結(jié)果傳到屏幕上顯示。
????????在 Android 里的那些資源組件的顯示(比如 Bitmaps 、 Drawable ),都是一起打包到統(tǒng)一的紋理( Texture )當(dāng)中,然后再傳遞到 GPU 里面。
????????圖片的顯示,則是先經(jīng)過 CPU 的計(jì)算加載到內(nèi)存中,再傳給 GPU 進(jìn)行渲染。
文字的顯示,則是先經(jīng)過 CPU 換算成紋理( Texture ),再傳給 GPU 進(jìn)行渲染,返回到 CPU 繪制單個(gè)字符的時(shí)候,再重新引用經(jīng)過 GPU 渲染的內(nèi)容。
????????動(dòng)畫的顯示更加復(fù)雜,我們需要在 16 ms 內(nèi)處理完所有 CPU 和 GPU 的計(jì)算、繪制、渲染等操作,才能獲得應(yīng)用的流暢體驗(yàn)。
二. To檢測和解決
2.1 檢測維度
????????根據(jù)業(yè)務(wù)的不同與所需要的測試粒度的不同,就會(huì)有不同的檢測維度。目前我所在業(yè)務(wù)所需的界面性能檢測維度如下:
界面過度繪制;(檢測過度繪制)
渲染性能;(檢測嚴(yán)格模式下的UI渲染性能呈現(xiàn))
布局邊界合理性;(檢測元素顯示的合理性)
????????還有專項(xiàng)測試中某些用戶場景可能還包含著另外一些隱形的檢測維度,比如:
OpenGL 跟蹤分析;
GPU 視圖更新合理性;
Flash 硬件層更新合理性;
動(dòng)畫加 / 減速狀態(tài)問題點(diǎn)檢測;
……
2.2 調(diào)試工具
????????檢測和解決界面性能問題很大程度上依賴于你的應(yīng)用程序架構(gòu),幸運(yùn)的是,Andorid 提供了很多調(diào)試工具,知道并學(xué)會(huì)使用這些工具很重要,它們可以幫助我們調(diào)試和分析界面性能問題,以讓應(yīng)用擁有更好的性能體驗(yàn)。下面列舉Android常見的界面性能調(diào)試工具:
2.2.1 Hierarchy View
????????Hierarchy View 在Android SDK里自帶,常用來查看界面的視圖結(jié)構(gòu)是否過于復(fù)雜,用于了解哪些視圖過度繪制,又該如何進(jìn)行改進(jìn)。詳見官方使用教程(需要翻墻):「?戳我?」,官方介紹「?戳我?」。
2.2.2 Lint
????????Lint 是 ADT 自帶的靜態(tài)代碼掃描工具,可以給 XML 布局文件和 項(xiàng)目代碼中不合理的或存在風(fēng)險(xiǎn)的模塊提出改善性建議。官方關(guān)于 Lint 的實(shí)際使用的提示,列舉幾點(diǎn)如下:
包含無用的分支,建議去除;
包含無用的父控件,建議去除;
警告該布局深度過深;
建議使用 compound drawables ;
建議使用?merge?標(biāo)簽;
……
2.2.3 Systrace
????????Systrace 在Android DDMS 里自帶,可以用來跟蹤 graphics 、view 和 window 的信息,發(fā)現(xiàn)一些深層次的問題。很麻煩,限制大,實(shí)際調(diào)試中我基本用不到。官方介紹 「戳我」和 「我」。
2.2.4 Track
????????Track 在 Android DDMS里自帶,是個(gè)很棒的用來跟蹤構(gòu)造視圖的時(shí)候哪些方法費(fèi)時(shí),精確到每一個(gè)函數(shù),無論是應(yīng)用函數(shù)還是系統(tǒng)函數(shù),我們可以很容易地看到掉幀的地方以及那一幀所有函數(shù)的調(diào)用情況,找出問題點(diǎn)進(jìn)行優(yōu)化。官方介紹 「戳我」。
2.2.5 OverDraw
????????通過在 Android 設(shè)備的設(shè)置 APP 的開發(fā)者選項(xiàng)里打開 “ 調(diào)試 GPU 過度繪制 ” ,來查看應(yīng)用所有界面及分支界面下的過度繪制情況,方便進(jìn)行優(yōu)化。官方介紹 「戳我」。
2.2.6 GPU 呈現(xiàn)模式分析
????????通過在 Android 設(shè)備的設(shè)置 APP 的開發(fā)者選項(xiàng)里啟動(dòng) “ GPU 呈現(xiàn)模式分析 ” ,可以得到最近 128 幀 每一幀渲染的時(shí)間,分析性能渲染的性能及性能瓶頸。官方介紹 「戳我」。
2.2.7 StrictMode
????????通過在 Android 設(shè)備的設(shè)置 APP 的開發(fā)者選項(xiàng)里啟動(dòng) “ 嚴(yán)格模式 ” ,來查看應(yīng)用哪些操作在主線程上執(zhí)行時(shí)間過長。當(dāng)一些操作違背了嚴(yán)格模式時(shí)屏幕的四周邊界會(huì)閃爍紅色,同時(shí)輸出 StrictMode 的相關(guān)信息到 LOGCAT 日志中。
2.2.8 Animator duration scale
????????通過在 Android 設(shè)備的設(shè)置 APP 的開發(fā)者選項(xiàng)里打開 “ 窗口動(dòng)畫縮放 ” / “ 過渡動(dòng)畫縮放 ” / “ 動(dòng)畫程序時(shí)長縮放 ”,來加速或減慢動(dòng)畫的時(shí)間,以查看加速或減慢狀態(tài)下的動(dòng)畫是否會(huì)有問題。
2.2.9 Show hardware layer updates
????????通過在 Android 設(shè)備的設(shè)置 APP 的開發(fā)者選項(xiàng)里啟動(dòng) “ 顯示硬件層更新 ”,當(dāng) Flash 硬件層在進(jìn)行更新時(shí)會(huì)顯示為綠色。使用這個(gè)工具可以讓你查看在動(dòng)畫期間哪些不期望更新的布局有更新,方便你進(jìn)行優(yōu)化,以獲得應(yīng)用更好的性能。實(shí)例《 Optimizing Android Hardware Layers 》(需要翻墻):「?戳我?」。
2.3 如何解決
????????前面提到過我司的目前所需的測試維度如下:
界面過度繪制;(檢測過度繪制)
渲染性能;(檢測嚴(yán)格模式下的UI渲染性能呈現(xiàn))
布局邊界合理性;(檢測元素顯示的合理性)
????????故接下來將圍繞這三兩點(diǎn),分別從概念、追蹤、挖掘根源以及排查的工具來具體講述如何解決,以及給開發(fā)的優(yōu)化建議。
三. 界面過度繪制(OverDraw)
3.1 過度繪制概念
????????過度繪制是一個(gè)術(shù)語,表示某些組件在屏幕上的一個(gè)像素點(diǎn)的繪制次數(shù)超過 1 次。
????????通俗來講,繪制界面可以類比成一個(gè)涂鴉客涂鴉墻壁,涂鴉是一件工作量很大的事情,墻面的每個(gè)點(diǎn)在涂鴉過程中可能被涂了各種各樣的顏色,但最終呈現(xiàn)的顏色卻只可能是 1 種。這意味著我們花大力氣涂鴉過程中那些非最終呈現(xiàn)的顏色對(duì)路人是不可見的,是一種對(duì)時(shí)間、精力和資源的浪費(fèi),存在很大的改善空間。繪制界面同理,花了太多的時(shí)間去繪制那些堆疊在下面的、用戶看不到的東西,這樣是在浪費(fèi)CPU周期和渲染時(shí)間!
????????官方例子,被用戶激活的卡片在最上面,而那些沒有激活的卡片在下面,在繪制用戶看不到的對(duì)象上花費(fèi)了太多的時(shí)間。
3.2 追蹤過度繪制
????????通過在 Android 設(shè)備的設(shè)置 APP 的開發(fā)者選項(xiàng)里打開 “ 調(diào)試 GPU 過度繪制 ” ,來查看應(yīng)用所有界面及分支界面下的過度繪制情況,方便進(jìn)行優(yōu)化。
????????Android 會(huì)在屏幕上顯示不同深淺的顏色來表示過度繪制:
沒顏色:沒有過度繪制,即一個(gè)像素點(diǎn)繪制了 1 次,顯示應(yīng)用本來的顏色;
藍(lán)色:1倍過度繪制,即一個(gè)像素點(diǎn)繪制了 2 次;
綠色:2倍過度繪制,即一個(gè)像素點(diǎn)繪制了 3 次;
淺紅色:3倍過度繪制,即一個(gè)像素點(diǎn)繪制了 4 次;
深紅色:4倍過度繪制及以上,即一個(gè)像素點(diǎn)繪制了 5 次及以上;
????????設(shè)備的硬件性能是有限的,當(dāng)過度繪制導(dǎo)致應(yīng)用需要消耗更多資源(超過了可用資源)的時(shí)候性能就會(huì)降低,表現(xiàn)為卡頓、不流暢、ANR 等。為了最大限度地提高應(yīng)用的性能和體驗(yàn),就需要盡可能地減少過度繪制,即更多的藍(lán)色色塊而不是紅色色塊。
????????實(shí)際測試,常用以下兩點(diǎn)來作為過度繪制的測試指標(biāo),將過度繪制控制在一個(gè)約定好的合理范圍內(nèi):
應(yīng)用所有界面以及分支界面均不存在超過4X過度繪制(深紅色區(qū)域);
應(yīng)用所有界面以及分支界面下,3X過度繪制總面積(淺紅色區(qū)域)不超過屏幕可視區(qū)域的1/4;
3.3 過度繪制的根源
????????過度繪制很大程度上來自于視圖相互重疊的問題,其次還有不必要的背景重疊。
????????官方例子,比如一個(gè)應(yīng)用所有的View都有背景的話,就會(huì)看起來像第一張圖中那樣,而在去除這些不必要的背景之后(指的是Window的默認(rèn)背景、Layout的背景、文字以及圖片的可能存在的背景),效果就像第二張圖那樣,基本沒有過度繪制的情況。
3.4 不合理的xml布局對(duì)繪制的影響
????????當(dāng)布局文件的節(jié)點(diǎn)樹的深度越深,XML 中的標(biāo)簽和屬性設(shè)置越多,對(duì)界面的顯示有災(zāi)難性影響。
????????一個(gè)界面要顯示出來,第一步會(huì)進(jìn)行解析布局,在 requestLayout 之后還要進(jìn)行一系列的 measure 、 layout 、 draw 操作,若布局文件嵌套過深、擁有的標(biāo)簽屬性過于臃腫,每一步的執(zhí)行時(shí)間都會(huì)受到影響,而界面的顯示是進(jìn)行完這些操作后才會(huì)顯示的,所以每一步操作的時(shí)間增長,最終顯示的時(shí)間就會(huì)越長。
3.5 源碼相關(guān)
????????有能力且有興趣看源碼的童鞋,過度繪制的源碼位置在: /frameworks/base/libs/hwui/OpenGLRenderer.cpp ,有興趣的可以去研究查看。
? ??if (Properties::debugOverdraw & getTargetFbo() == 0) {
????????const Rect* clip = &mTilingClip;
????????mRenderState.scissor().setEnabled(true);
????????mRenderState.scissor().set(clip->left,
????????????????mState.firstSnapshot()->getViewportHeight() - clip->bottom,
????????????????clip->right - clip->left,
????????????????clip->bottom - clip->top);
?
????????// 1x overdraw
????????mRenderState.stencil().enableDebugTest(2);
????????drawColor(mCaches.getOverdrawColor(1), SkXfermode::kSrcOver_Mode);
?
????????// 2x overdraw
????????mRenderState.stencil().enableDebugTest(3);
????????drawColor(mCaches.getOverdrawColor(2), SkXfermode::kSrcOver_Mode);
?
????????// 3x overdraw
????????mRenderState.stencil().enableDebugTest(4);
????????drawColor(mCaches.getOverdrawColor(3), SkXfermode::kSrcOver_Mode);
?
????????// 4x overdraw and higher
????????mRenderState.stencil().enableDebugTest(4, true);
????????drawColor(mCaches.getOverdrawColor(4), SkXfermode::kSrcOver_Mode);
?
????????mRenderState.stencil().disable();
????}
}
四. 渲染性能(Rendering)
4.1 渲染性能概念
????????渲染性能往往是掉幀的罪魁禍?zhǔn)祝@種問題很常見,讓人頭疼。好在 Android 給我們提供了一個(gè)強(qiáng)大的工具,幫助我們非常容易追蹤性能渲染問題,看到究竟是什么導(dǎo)致你的應(yīng)用出現(xiàn)卡頓、掉幀。
4.2 追蹤渲染性能
????????通過在 Android 設(shè)備的設(shè)置 APP 的開發(fā)者選項(xiàng)里打開 “ GPU 呈現(xiàn)模式分析 ” 選項(xiàng),選擇 ” 在屏幕上顯示為條形圖 “ 。
????????這個(gè)工具會(huì)在Android 設(shè)備的屏幕上實(shí)時(shí)顯示當(dāng)前界面的最近 128 幀 的 GPU 繪制圖形數(shù)據(jù),包括 StatusBar 、 NavBar 、 當(dāng)前界面的 GPU 繪制圖形柱狀圖數(shù)據(jù)。我們一般只需關(guān)心當(dāng)前界面的 GPU 繪制圖形數(shù)據(jù)即可。
????????界面上一共有 128 個(gè)小柱狀圖,代表的是當(dāng)前界面最近的 128 幀 GPU 繪制圖形數(shù)據(jù)。一個(gè)小柱狀圖代表的這一幀畫面渲染的耗時(shí),柱狀圖越高代表耗時(shí)越長。隨著界面的刷新,柱狀圖信息也會(huì)實(shí)時(shí)滾動(dòng)刷新。
????????中間有一條綠線,代表 16 ms ,保持動(dòng)畫流暢的關(guān)鍵就在于讓這些垂直的柱狀條盡可能地保持在綠線下面,任何時(shí)候超過綠線,你就有可能丟失一幀的內(nèi)容。
????????每一個(gè)柱狀圖都是由三種顏色構(gòu)成:藍(lán)、紅、黃。
-
藍(lán)色代表的是這一幀繪制 Display List 的時(shí)間。通俗來說,就是記錄了需要花費(fèi)多長時(shí)間在屏幕上更新視圖。用代碼語言來說,就是執(zhí)行視圖的 onDraw 方法,創(chuàng)建或更新每一個(gè)視圖的 Display List 的時(shí)間。
-
紅色代表的是這一幀 OpenGL 渲染 Display List 所需要的時(shí)間。通俗來說,就是記錄了執(zhí)行視圖繪制的耗時(shí)。用代碼語言來說,就是 Android 用 OpenGL ES 的 API 接口進(jìn)行 2D 渲染 Display List 的時(shí)間。
黃色代表的是這一幀 CPU 等待 GPU 處理的時(shí)間。通俗來說,就是 CPU 等待 GPU 發(fā)出接到命令的回復(fù)的等待時(shí)間。用代碼語言來說,就是這是一個(gè)阻塞調(diào)用。
????????實(shí)際測試,常用以下兩點(diǎn)來作為渲染性能的測試指標(biāo),將渲染性能控制在一個(gè)約定好的合理范圍內(nèi):
執(zhí)行應(yīng)用的所有功能及分支功能,操作過程中涉及的柱狀條區(qū)域應(yīng)至少 90 % 保持到綠線下面;
從用戶體檢的角度主觀判斷應(yīng)用在 512 M 內(nèi)存的 Android 設(shè)備下所有操作過程中的卡頓感是否能接受,不會(huì)感覺突兀怪異;
4.3 渲染性能差的根源
????????當(dāng)你看到藍(lán)色的線較高的時(shí)候,可能是由于你的視圖突然無效了需要重新繪制,或者是自定義的視圖過于復(fù)雜耗時(shí)過長。
????????當(dāng)你看到紅色的線較高的時(shí)候,可能是由于你的視圖重新提交了需要重新繪制導(dǎo)致的(比如屏幕從豎屏旋轉(zhuǎn)成橫屏后當(dāng)前界面重新創(chuàng)建),或者是自定義的視圖很復(fù)雜,繪制起來很麻煩,導(dǎo)致耗時(shí)過長。比如下面這種視圖:
????????當(dāng)你看到黃色的線較高的時(shí)候,那就意味著你給 GPU 太多的工作,太多的負(fù)責(zé)視圖需要 OpenGL 命令去繪制和處理,導(dǎo)致 CPU 遲遲沒等到 GPU 發(fā)出接到命令的回復(fù)。
4.4 檢測說明
????????這個(gè)工具能夠很好地幫助你找到渲染相關(guān)的問題,幫助你找到卡頓的性能瓶頸,追蹤究竟是什么導(dǎo)致被測應(yīng)用出現(xiàn)卡頓、變慢的情況,以便在代碼層面進(jìn)行優(yōu)化。甚至讓負(fù)責(zé)產(chǎn)品設(shè)計(jì)的人去改善他的設(shè)計(jì),以獲得良好的用戶體驗(yàn)。
????????檢測渲染性能時(shí),常伴隨著開啟“ 嚴(yán)格模式 ” 查看應(yīng)用哪些情景在 UI 線程(主線程)上執(zhí)行時(shí)間過長。
????????另外有些強(qiáng)大但可能少用的工具在測試性能渲染時(shí)輔助分析,比如:
HierarchyViewer:這個(gè)工具常用來查看界面的視圖結(jié)構(gòu)是否過于復(fù)雜,用于了解哪些視圖過度繪制,又該如何進(jìn)行改進(jìn);
Tracer for OpenGL:這個(gè)工具收集了所有UI界面發(fā)給GPU的繪制命令。常用于輔助開發(fā)人員 DEBUG 、定位一些 HierarchyViewer 工具定位不了的疑難渲染細(xì)節(jié)問題。
4.5 UI繪制機(jī)制的補(bǔ)充說明
????????如上面所說,布局和 UI 組件等都會(huì)先經(jīng)過 CPU 計(jì)算成 GPU 能夠識(shí)別并繪制的多邊形( Polygons ),紋理( Texture ),然后交給 GPU 進(jìn)行柵格化渲染,再將處理結(jié)果傳到屏幕上顯示。 “ CPU 計(jì)算成 GPU 能夠識(shí)別并繪制的對(duì)象 ” 這個(gè)操作是在 DisplayList 的幫助下完成的。DisplayList 擁有要交給 GPU 柵格化渲染到屏幕上的數(shù)據(jù)信息。
????????DisplayList 會(huì)在某個(gè)視圖第一次需要渲染時(shí)創(chuàng)建。當(dāng)該視圖有類似位置被移動(dòng)等變化而需要重新渲染這個(gè)視圖的時(shí)候,則只需 GPU 額外執(zhí)行一次渲染指令冰更新到屏幕上就夠了。但如果視圖中的繪制內(nèi)容發(fā)生變化時(shí)(比如不可見了),那之間的 DisplayList 就無法繼續(xù)使用了,這時(shí)系統(tǒng)就會(huì)重新執(zhí)行一次重新創(chuàng)建 DisplayList 、渲染DisplayList 并更新到屏幕上。這個(gè)流程的表現(xiàn)性能取決于該視圖的復(fù)雜程度。
五. 布局邊界合理性
????????這一章節(jié)比較簡單,考慮到應(yīng)該沒受眾,故不展開討論。
六. 給開發(fā)的界面優(yōu)化 Advice
6.1 優(yōu)化布局的結(jié)構(gòu)
????????布局結(jié)構(gòu)太復(fù)雜,會(huì)減慢渲染的速度,造成性能瓶頸。我們可以通過以下這些慣用、有效的布局原則來優(yōu)化:
-
避免復(fù)雜的View層級(jí)。布局越復(fù)雜就越臃腫,就越容易出現(xiàn)性能問題,尋找最節(jié)省資源的方式去展示嵌套的內(nèi)容;
-
盡量避免在視圖層級(jí)的頂層使用相對(duì)布局?RelativeLayout?。相對(duì)布局?RelativeLayout?比較耗資源,因?yàn)橐粋€(gè)相對(duì)布局?RelativeLayout?需要兩次度量來確保自己處理了所有的布局關(guān)系,而且這個(gè)問題會(huì)伴隨著視圖層級(jí)中的相對(duì)布局?RelativeLayout?的增多,而變得更嚴(yán)重;
-
布局層級(jí)一樣的情況建議使用線性布局?LinearLayout?代替相對(duì)布局?RelativeLayout,因?yàn)榫€性布局?LinearLayout?性能要更高一些;確實(shí)需要對(duì)分支進(jìn)行相對(duì)布局?RelativeLayout?的時(shí)候,可以考慮更優(yōu)化的網(wǎng)格布局?GridLayout?,它已經(jīng)預(yù)處理了分支視圖的關(guān)系,可以避免兩次度量的問題;
-
相對(duì)復(fù)雜的布局建議采用相對(duì)布局?RelativeLayout?,相對(duì)布局?RelativeLayout?可以簡單實(shí)現(xiàn)線性布局?LinearLayout?嵌套才能實(shí)現(xiàn)的布局;
-
不要使用絕對(duì)布局?AbsoluteLayout?;
-
將可重復(fù)使用的組件抽取出來并用?標(biāo)簽進(jìn)行重用。如果應(yīng)用多個(gè)地方的 UI 用到某個(gè)布局,就將其寫成一個(gè)布局部件,便于各個(gè) UI 重用。官方詳解 「?戳我?」
-
使用?merge?標(biāo)簽減少布局的嵌套層次,官方詳解 「?戳我?」;
-
去掉多余的不可見背景。有多層背景顏色的布局,只留最上層的對(duì)用戶可見的顏色即可,其他用戶不可見的底層顏色可以去掉,減少無效的繪制操作;
-
盡量避免使用 layoutweight 屬性。使用包含 layoutweight 屬性的線性布局?LinearLayout?每一個(gè)子組件都需要被測量兩次,會(huì)消耗過多的系統(tǒng)資源。在使用?ListView?標(biāo)簽與?GridView?標(biāo)簽的時(shí)候,這個(gè)問題顯的尤其重要,因?yàn)樽咏M件會(huì)重復(fù)被創(chuàng)建。平分布局可以使用相對(duì)布局?RelativeLayout?里一個(gè) 0dp 的 view 做分割線來搞定,如果不行,那就……;
合理的界面的布局結(jié)構(gòu)應(yīng)是寬而淺,而不是窄而深;
6.2 優(yōu)化處理邏輯
-
按需載入視圖。某些不怎么重用的耗資源視圖,可以等到需要的時(shí)候再加載,提高UI渲染速度;
-
使用?ViewStub?標(biāo)簽來加載一些不常用的布局;
-
動(dòng)態(tài)地 inflation view 性能要比用?ViewStub?標(biāo)簽的 setVisiblity 性能要好,當(dāng)然某些功能的實(shí)現(xiàn)采用?ViewStub?標(biāo)簽更合適;
-
盡量避免不必要的耗資源操作,節(jié)省寶貴的運(yùn)算時(shí)間;
-
避免在 UI 線程進(jìn)行繁重的操作。耗資源的操作(比如 IO 操作、網(wǎng)絡(luò)操作、SQL 操作、列表刷新等)耗資源的操作應(yīng)用后臺(tái)進(jìn)程去實(shí)現(xiàn),不能占用 UI 線程,UI 線程是主線程,主線程是保持程序流暢的關(guān)鍵,應(yīng)該只操作那些核心的 UI 操作,比如處理視圖的屬性和繪制;
-
最小化喚醒機(jī)制。我們常用廣播來接收那些期望響應(yīng)的消息和事件,但過多的響應(yīng)超過本身需求的話,會(huì)消耗多余的 Android 設(shè)備性能和資源。所以應(yīng)該最小化喚醒機(jī)制,當(dāng)應(yīng)用不關(guān)心這些消失和事件時(shí),就關(guān)閉廣播,并慎重選擇那些要響應(yīng)的 Intent 。
-
為低端設(shè)備考慮,比如 512M 內(nèi)存、雙核 CPU 、低分辨率,確保你的應(yīng)用可以滿足不同水平的設(shè)備。
優(yōu)化應(yīng)用的啟動(dòng)速度。當(dāng)應(yīng)用啟動(dòng)一個(gè)應(yīng)用時(shí),界面的盡快反饋顯示可以給用戶一個(gè)良好的體驗(yàn)。為了啟動(dòng)更快,可以延遲加載一些 UI 以及避免在應(yīng)用?Application?層級(jí)初始化代碼。
6.3 善用 DEBUG 工具
多使用Android提供的一些調(diào)試工具去追蹤應(yīng)用主要功能的性能情況;
多使用Android提供的一些調(diào)試工具去追蹤應(yīng)用主要功能的內(nèi)存分配情況;
來源:http://www.apkbus.com/blog-873057-72671.html