Android界面性能優(yōu)化最全總結(jié)、原理剖析,必讀!


? ? 界面是 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



最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,702評(píng)論 6 534
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,615評(píng)論 3 419
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,606評(píng)論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,044評(píng)論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,826評(píng)論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,227評(píng)論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,307評(píng)論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,447評(píng)論 0 289
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,992評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,807評(píng)論 3 355
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,001評(píng)論 1 370
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,550評(píng)論 5 361
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,243評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,667評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,930評(píng)論 1 287
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,709評(píng)論 3 393
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,996評(píng)論 2 374

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,665評(píng)論 25 708
  • 界面是 Android 應(yīng)用中直接影響用戶體驗(yàn)最關(guān)鍵的部分。如果代碼實(shí)現(xiàn)得不好,界面容易發(fā)生卡頓且導(dǎo)致應(yīng)用占用大量...
    Ten_Minutes閱讀 692評(píng)論 0 9
  • 回首過去,我們每個(gè)人都經(jīng)歷了太多太多的第一次,第一聲啼哭、第一次笑、第一次開口說話、第一次站、第一次邁步走、第一次...
    不嫌事大閱讀 479評(píng)論 1 1
  • 總是 尋找著最好的自己 卻不知 越走越是迷...
    澄默閱讀 207評(píng)論 0 1
  • 今天去看了戰(zhàn)狼2,戰(zhàn)狼系列的主題有所不同 戰(zhàn)狼1的主題是任何非法侵入我國領(lǐng)土者,雖遠(yuǎn)必誅!而戰(zhàn)狼2的主題是,我們?nèi)?..
    Mint黎閱讀 285評(píng)論 0 3