? ? 界面是 Android 應(yīng)用中直接影響用戶體驗(yàn)最關(guān)鍵的部分。如果代碼實(shí)現(xiàn)得不好朵逝,界面容易發(fā)生卡頓且導(dǎo)致應(yīng)用占用大量?jī)?nèi)存蔚袍。
????????我司這類做 ROM 的公司更不一樣,預(yù)裝的應(yīng)用一定要非常流暢配名,這樣給客戶或用戶的第一感覺(jué)就是快啤咽。又卡又慢的應(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ī)制與柵格化
二. 檢測(cè)和解決
2.1 檢測(cè)維度
2.2 調(diào)試工具
2.3 如何解決
三. 界面過(guò)度繪制
3.1 過(guò)度繪制概念
3.2 追蹤過(guò)度繪制
3.3 過(guò)度繪制的根源
3.4 不合理的xml布局對(duì)繪制的影響
3.5 源碼相關(guān)
四. 渲染性能
4.1 渲染性能概念
4.2 追蹤渲染性能
4.3 渲染性能差的根源
4.4 檢測(cè)說(shuō)明
4.5 UI繪制機(jī)制的補(bǔ)充說(shuō)明
五. 布局邊界合理性
六. 給開發(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)畫和輸入事件的過(guò)程中以約 60 幀每秒( 1秒 / 0.016幀每秒 = 62.5幀/秒 )的平滑幀率來(lái)渲染索赏。
????????如果你的應(yīng)用沒(méi)有在 16ms 內(nèi)完成這一幀的繪制盼玄,假設(shè)你花了 24ms 來(lái)繪制這一幀,那么就會(huì)出現(xiàn)掉幀的情況潜腻。
????????系統(tǒng)準(zhǔn)備將新的一幀繪制到屏幕上埃儿,但是這一幀并沒(méi)有準(zhǔn)備好,所有就不會(huì)有繪制操作融涣,畫面也就不會(huì)刷新童番。反饋到用戶身上,就是用戶盯著同一張圖看了 32ms 而不是 16ms 威鹿,也就是說(shuō)掉幀發(fā)生了剃斧。
1.2 掉幀
????????掉幀是用戶體驗(yàn)中一個(gè)非常核心的問(wèn)題。丟棄了當(dāng)前幀忽你,并且之后不能夠延續(xù)之前的幀率幼东,這種不連續(xù)的間隔會(huì)容易會(huì)引起用戶的注意,也就是我們常說(shuō)的卡頓科雳、不流暢根蟹。
????????引起掉幀的原因非常多,比如:
花了非常多時(shí)間重新繪制界面中的大部分東西糟秘,這樣非常浪費(fèi)CPU周期简逮;
過(guò)度繪制嚴(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來(lái)作為 App 性能的衡量標(biāo)準(zhǔn)呢丑婿?這是因?yàn)槿搜酆痛竽X之間的協(xié)作無(wú)法感知到超過(guò) 60 Fps的畫面更新。
????????市面上絕大多數(shù)Android設(shè)備的屏幕刷新頻率是 60 HZ没卸。當(dāng)然,超過(guò) 60 Fps 是沒(méi)有意義的秒旋,人眼感知不到區(qū)別约计。24 Fps 是人眼能感知的連續(xù)線性的運(yùn)動(dòng),所以是電影膠圈的常用幀率迁筛,因?yàn)檫@個(gè)幀率已經(jīng)足夠支撐大部分電影畫面所要表達(dá)的內(nèi)容煤蚌,同時(shí)能最大限度地減少費(fèi)用支出。但是细卧,低于 30 Fps 是無(wú)法順暢表現(xiàn)絢麗的畫面內(nèi)容的尉桩,此時(shí)就需要用到 60 Fps 來(lái)達(dá)到想要表達(dá)的效果。了解更多Fps知識(shí)詳見(jiàn)「?Wiki?」贪庙。
????????應(yīng)用的界面性能目標(biāo)就是保持 60 Fps蜘犁,這意味著每一幀你只有 16 ms(1秒 / 60幀率)的時(shí)間來(lái)處理所有的任務(wù)。
1.4 垃圾回收
????????垃圾回收器是一個(gè)在應(yīng)用運(yùn)行期間自動(dòng)釋放那些不再引用的內(nèi)存的機(jī)制止邮,常稱 GC 这橙。頻繁的 GC 也是導(dǎo)致嚴(yán)重性能問(wèn)題的罪魁禍?zhǔn)字弧?/span>
????????前面提到,平滑的完成一幀意味著所有渲染代碼都必須在 16ms 內(nèi)完成导披。頻繁的 GC 會(huì)嚴(yán)重限制一幀時(shí)間內(nèi)的剩余時(shí)間屈扎,如果 GC 所做的工作超過(guò)了那些必須的工作,那么留給應(yīng)用平滑的幀率的時(shí)間就越少撩匕。越接近 16ms 鹰晨,在垃圾回收事件觸發(fā)的時(shí)候,就越容易導(dǎo)致卡頓止毕。
????????注意模蜡,Android4.4 引進(jìn)了新的 ART 虛擬機(jī)來(lái)取代 Dalvik 虛擬機(jī)。它們的機(jī)制大有不同滓技,簡(jiǎn)單而言:
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ī)來(lái)說(shuō)的垃圾回收來(lái)說(shuō)有一個(gè)很大的性能提升荚孵,但 2 – 3 ms 的回收時(shí)間對(duì)于超過(guò)16ms幀率的界限也是足夠的妹窖。因此,盡管垃圾回收在 Android 5.0 之后不再是耗資源的行為收叶,但也是始終需要盡可能避免的骄呼,特別是在執(zhí)行動(dòng)畫的情況下,可能會(huì)導(dǎo)致一些讓用戶明顯感覺(jué)的丟幀判没。
????????想了解更多詳細(xì)的 ART 和 Dalvik 虛擬機(jī)垃圾回收機(jī)制蜓萄,可「?戳我?」和「?我?」進(jìn)行深入了解。
1.5 UI 線程
????????UI 線程是應(yīng)用的主線程澄峰,很多的性能和卡頓問(wèn)題是由于我們?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 通過(guò) 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ì)很明顯地感覺(jué)到動(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ì)帶來(lái)多余的 CPU 計(jì)算操作曾雕,還會(huì)占用多余的 GPU 資源。
????????柵格化( Rasterization )能將 Button 助被、 Shape 剖张、 Path 、 Bitmap 等資源組件拆分到不同的像素上進(jìn)行顯示揩环。這個(gè)操作很費(fèi)時(shí)搔弄,所以引入了 GPU 來(lái)加快柵格化的操作。
????????CPU 負(fù)責(zé)把 UI 組件計(jì)算成多邊形( Polygons )丰滑,紋理( Texture )顾犹,然后交給 GPU 進(jìn)行柵格化渲染,再將處理結(jié)果傳到屏幕上顯示褒墨。
????????在 Android 里的那些資源組件的顯示(比如 Bitmaps 炫刷、 Drawable ),都是一起打包到統(tǒng)一的紋理( Texture )當(dāng)中郁妈,然后再傳遞到 GPU 里面浑玛。
????????圖片的顯示,則是先經(jīng)過(guò) CPU 的計(jì)算加載到內(nèi)存中噩咪,再傳給 GPU 進(jìn)行渲染顾彰。
文字的顯示极阅,則是先經(jīng)過(guò) CPU 換算成紋理( Texture ),再傳給 GPU 進(jìn)行渲染拘央,返回到 CPU 繪制單個(gè)字符的時(shí)候涂屁,再重新引用經(jīng)過(guò) GPU 渲染的內(nèi)容。
????????動(dòng)畫的顯示更加復(fù)雜灰伟,我們需要在 16 ms 內(nèi)處理完所有 CPU 和 GPU 的計(jì)算拆又、繪制、渲染等操作栏账,才能獲得應(yīng)用的流暢體驗(yàn)帖族。
二. To檢測(cè)和解決
2.1 檢測(cè)維度
????????根據(jù)業(yè)務(wù)的不同與所需要的測(cè)試粒度的不同,就會(huì)有不同的檢測(cè)維度挡爵。目前我所在業(yè)務(wù)所需的界面性能檢測(cè)維度如下:
界面過(guò)度繪制竖般;(檢測(cè)過(guò)度繪制)
渲染性能;(檢測(cè)嚴(yán)格模式下的UI渲染性能呈現(xiàn))
布局邊界合理性茶鹃;(檢測(cè)元素顯示的合理性)
????????還有專項(xiàng)測(cè)試中某些用戶場(chǎng)景可能還包含著另外一些隱形的檢測(cè)維度涣雕,比如:
OpenGL 跟蹤分析;
GPU 視圖更新合理性闭翩;
Flash 硬件層更新合理性挣郭;
動(dòng)畫加 / 減速狀態(tài)問(wèn)題點(diǎn)檢測(cè);
……
2.2 調(diào)試工具
????????檢測(cè)和解決界面性能問(wèn)題很大程度上依賴于你的應(yīng)用程序架構(gòu)疗韵,幸運(yùn)的是兑障,Andorid 提供了很多調(diào)試工具,知道并學(xué)會(huì)使用這些工具很重要蕉汪,它們可以幫助我們調(diào)試和分析界面性能問(wèn)題流译,以讓應(yīng)用擁有更好的性能體驗(yàn)。下面列舉Android常見(jiàn)的界面性能調(diào)試工具:
2.2.1 Hierarchy View
????????Hierarchy View 在Android SDK里自帶者疤,常用來(lái)查看界面的視圖結(jié)構(gòu)是否過(guò)于復(fù)雜福澡,用于了解哪些視圖過(guò)度繪制,又該如何進(jìn)行改進(jìn)宛渐。詳見(jiàn)官方使用教程(需要翻墻):「?戳我?」竞漾,官方介紹「?戳我?」。
2.2.2 Lint
????????Lint 是 ADT 自帶的靜態(tài)代碼掃描工具窥翩,可以給 XML 布局文件和 項(xiàng)目代碼中不合理的或存在風(fēng)險(xiǎn)的模塊提出改善性建議业岁。官方關(guān)于 Lint 的實(shí)際使用的提示,列舉幾點(diǎn)如下:
包含無(wú)用的分支寇蚊,建議去除笔时;
包含無(wú)用的父控件,建議去除仗岸;
警告該布局深度過(guò)深允耿;
建議使用 compound drawables 借笙;
建議使用?merge?標(biāo)簽;
……
2.2.3 Systrace
????????Systrace 在Android DDMS 里自帶较锡,可以用來(lái)跟蹤 graphics 业稼、view 和 window 的信息,發(fā)現(xiàn)一些深層次的問(wèn)題蚂蕴。很麻煩低散,限制大,實(shí)際調(diào)試中我基本用不到骡楼。官方介紹 「戳我」和 「我」熔号。
2.2.4 Track
????????Track 在 Android DDMS里自帶,是個(gè)很棒的用來(lái)跟蹤構(gòu)造視圖的時(shí)候哪些方法費(fèi)時(shí)鸟整,精確到每一個(gè)函數(shù)引镊,無(wú)論是應(yīng)用函數(shù)還是系統(tǒng)函數(shù),我們可以很容易地看到掉幀的地方以及那一幀所有函數(shù)的調(diào)用情況篮条,找出問(wèn)題點(diǎn)進(jìn)行優(yōu)化弟头。官方介紹 「戳我」。
2.2.5 OverDraw
????????通過(guò)在 Android 設(shè)備的設(shè)置 APP 的開發(fā)者選項(xiàng)里打開 “ 調(diào)試 GPU 過(guò)度繪制 ” 涉茧,來(lái)查看應(yīng)用所有界面及分支界面下的過(guò)度繪制情況亮瓷,方便進(jìn)行優(yōu)化。官方介紹 「戳我」降瞳。
2.2.6 GPU 呈現(xiàn)模式分析
????????通過(guò)在 Android 設(shè)備的設(shè)置 APP 的開發(fā)者選項(xiàng)里啟動(dòng) “ GPU 呈現(xiàn)模式分析 ” ,可以得到最近 128 幀 每一幀渲染的時(shí)間蚓胸,分析性能渲染的性能及性能瓶頸挣饥。官方介紹 「戳我」。
2.2.7 StrictMode
????????通過(guò)在 Android 設(shè)備的設(shè)置 APP 的開發(fā)者選項(xiàng)里啟動(dòng) “ 嚴(yán)格模式 ” 沛膳,來(lái)查看應(yīng)用哪些操作在主線程上執(zhí)行時(shí)間過(guò)長(zhǎng)扔枫。當(dāng)一些操作違背了嚴(yán)格模式時(shí)屏幕的四周邊界會(huì)閃爍紅色,同時(shí)輸出 StrictMode 的相關(guān)信息到 LOGCAT 日志中锹安。
2.2.8 Animator duration scale
????????通過(guò)在 Android 設(shè)備的設(shè)置 APP 的開發(fā)者選項(xiàng)里打開 “ 窗口動(dòng)畫縮放 ” / “ 過(guò)渡動(dòng)畫縮放 ” / “ 動(dòng)畫程序時(shí)長(zhǎng)縮放 ”短荐,來(lái)加速或減慢動(dòng)畫的時(shí)間,以查看加速或減慢狀態(tài)下的動(dòng)畫是否會(huì)有問(wèn)題叹哭。
2.2.9 Show hardware layer updates
????????通過(guò)在 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 如何解決
????????前面提到過(guò)我司的目前所需的測(cè)試維度如下:
界面過(guò)度繪制哺徊;(檢測(cè)過(guò)度繪制)
渲染性能;(檢測(cè)嚴(yán)格模式下的UI渲染性能呈現(xiàn))
布局邊界合理性乾闰;(檢測(cè)元素顯示的合理性)
????????故接下來(lái)將圍繞這三兩點(diǎn)落追,分別從概念、追蹤涯肩、挖掘根源以及排查的工具來(lái)具體講述如何解決轿钠,以及給開發(fā)的優(yōu)化建議。
三. 界面過(guò)度繪制(OverDraw)
3.1 過(guò)度繪制概念
????????過(guò)度繪制是一個(gè)術(shù)語(yǔ)宽菜,表示某些組件在屏幕上的一個(gè)像素點(diǎn)的繪制次數(shù)超過(guò) 1 次谣膳。
????????通俗來(lái)講,繪制界面可以類比成一個(gè)涂鴉客涂鴉墻壁铅乡,涂鴉是一件工作量很大的事情继谚,墻面的每個(gè)點(diǎn)在涂鴉過(guò)程中可能被涂了各種各樣的顏色,但最終呈現(xiàn)的顏色卻只可能是 1 種阵幸。這意味著我們花大力氣涂鴉過(guò)程中那些非最終呈現(xiàn)的顏色對(duì)路人是不可見(jiàn)的花履,是一種對(duì)時(shí)間、精力和資源的浪費(fèi)挚赊,存在很大的改善空間诡壁。繪制界面同理,花了太多的時(shí)間去繪制那些堆疊在下面的荠割、用戶看不到的東西妹卿,這樣是在浪費(fèi)CPU周期和渲染時(shí)間!
????????官方例子蔑鹦,被用戶激活的卡片在最上面,而那些沒(méi)有激活的卡片在下面夺克,在繪制用戶看不到的對(duì)象上花費(fèi)了太多的時(shí)間。
3.2 追蹤過(guò)度繪制
????????通過(guò)在 Android 設(shè)備的設(shè)置 APP 的開發(fā)者選項(xiàng)里打開 “ 調(diào)試 GPU 過(guò)度繪制 ” 嚎朽,來(lái)查看應(yīng)用所有界面及分支界面下的過(guò)度繪制情況铺纽,方便進(jìn)行優(yōu)化。
????????Android 會(huì)在屏幕上顯示不同深淺的顏色來(lái)表示過(guò)度繪制:
沒(méi)顏色:沒(méi)有過(guò)度繪制哟忍,即一個(gè)像素點(diǎn)繪制了 1 次狡门,顯示應(yīng)用本來(lái)的顏色;
藍(lán)色:1倍過(guò)度繪制锅很,即一個(gè)像素點(diǎn)繪制了 2 次其馏;
綠色:2倍過(guò)度繪制,即一個(gè)像素點(diǎn)繪制了 3 次爆安;
淺紅色:3倍過(guò)度繪制尝偎,即一個(gè)像素點(diǎn)繪制了 4 次;
深紅色:4倍過(guò)度繪制及以上,即一個(gè)像素點(diǎn)繪制了 5 次及以上致扯;
????????設(shè)備的硬件性能是有限的肤寝,當(dāng)過(guò)度繪制導(dǎo)致應(yīng)用需要消耗更多資源(超過(guò)了可用資源)的時(shí)候性能就會(huì)降低,表現(xiàn)為卡頓抖僵、不流暢鲤看、ANR 等。為了最大限度地提高應(yīng)用的性能和體驗(yàn)耍群,就需要盡可能地減少過(guò)度繪制义桂,即更多的藍(lán)色色塊而不是紅色色塊。
????????實(shí)際測(cè)試蹈垢,常用以下兩點(diǎn)來(lái)作為過(guò)度繪制的測(cè)試指標(biāo)慷吊,將過(guò)度繪制控制在一個(gè)約定好的合理范圍內(nèi):
應(yīng)用所有界面以及分支界面均不存在超過(guò)4X過(guò)度繪制(深紅色區(qū)域);
應(yīng)用所有界面以及分支界面下曹抬,3X過(guò)度繪制總面積(淺紅色區(qū)域)不超過(guò)屏幕可視區(qū)域的1/4溉瓶;
3.3 過(guò)度繪制的根源
????????過(guò)度繪制很大程度上來(lái)自于視圖相互重疊的問(wèn)題,其次還有不必要的背景重疊谤民。
????????官方例子堰酿,比如一個(gè)應(yīng)用所有的View都有背景的話,就會(huì)看起來(lái)像第一張圖中那樣张足,而在去除這些不必要的背景之后(指的是Window的默認(rèn)背景触创、Layout的背景、文字以及圖片的可能存在的背景)为牍,效果就像第二張圖那樣哼绑,基本沒(méi)有過(guò)度繪制的情況。
3.4 不合理的xml布局對(duì)繪制的影響
????????當(dāng)布局文件的節(jié)點(diǎn)樹的深度越深碉咆,XML 中的標(biāo)簽和屬性設(shè)置越多凌那,對(duì)界面的顯示有災(zāi)難性影響。
????????一個(gè)界面要顯示出來(lái)吟逝,第一步會(huì)進(jìn)行解析布局,在 requestLayout 之后還要進(jìn)行一系列的 measure 赦肋、 layout 块攒、 draw 操作,若布局文件嵌套過(guò)深佃乘、擁有的標(biāo)簽屬性過(guò)于臃腫囱井,每一步的執(zhí)行時(shí)間都會(huì)受到影響,而界面的顯示是進(jìn)行完這些操作后才會(huì)顯示的趣避,所以每一步操作的時(shí)間增長(zhǎng)庞呕,最終顯示的時(shí)間就會(huì)越長(zhǎng)。
3.5 源碼相關(guān)
????????有能力且有興趣看源碼的童鞋,過(guò)度繪制的源碼位置在: /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)祝@種問(wèn)題很常見(jiàn)时捌,讓人頭疼店诗。好在 Android 給我們提供了一個(gè)強(qiáng)大的工具,幫助我們非常容易追蹤性能渲染問(wèn)題盏混,看到究竟是什么導(dǎo)致你的應(yīng)用出現(xiàn)卡頓蔚鸥、掉幀。
4.2 追蹤渲染性能
????????通過(guò)在 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í)越長(zhǎng)鳖粟。隨著界面的刷新,柱狀圖信息也會(huì)實(shí)時(shí)滾動(dòng)刷新拙绊。
????????中間有一條綠線向图,代表 16 ms ,保持動(dòng)畫流暢的關(guān)鍵就在于讓這些垂直的柱狀條盡可能地保持在綠線下面,任何時(shí)候超過(guò)綠線,你就有可能丟失一幀的內(nèi)容标沪。
????????每一個(gè)柱狀圖都是由三種顏色構(gòu)成:藍(lán)榄攀、紅、黃金句。
-
藍(lán)色代表的是這一幀繪制 Display List 的時(shí)間檩赢。通俗來(lái)說(shuō),就是記錄了需要花費(fèi)多長(zhǎng)時(shí)間在屏幕上更新視圖违寞。用代碼語(yǔ)言來(lái)說(shuō)贞瞒,就是執(zhí)行視圖的 onDraw 方法,創(chuàng)建或更新每一個(gè)視圖的 Display List 的時(shí)間趁曼。
-
紅色代表的是這一幀 OpenGL 渲染 Display List 所需要的時(shí)間军浆。通俗來(lái)說(shuō),就是記錄了執(zhí)行視圖繪制的耗時(shí)挡闰。用代碼語(yǔ)言來(lái)說(shuō)乒融,就是 Android 用 OpenGL ES 的 API 接口進(jìn)行 2D 渲染 Display List 的時(shí)間掰盘。
黃色代表的是這一幀 CPU 等待 GPU 處理的時(shí)間。通俗來(lái)說(shuō)赞季,就是 CPU 等待 GPU 發(fā)出接到命令的回復(fù)的等待時(shí)間愧捕。用代碼語(yǔ)言來(lái)說(shuō),就是這是一個(gè)阻塞調(diào)用碟摆。
????????實(shí)際測(cè)試晃财,常用以下兩點(diǎn)來(lái)作為渲染性能的測(cè)試指標(biāo),將渲染性能控制在一個(gè)約定好的合理范圍內(nèi):
執(zhí)行應(yīng)用的所有功能及分支功能典蜕,操作過(guò)程中涉及的柱狀條區(qū)域應(yīng)至少 90 % 保持到綠線下面断盛;
從用戶體檢的角度主觀判斷應(yīng)用在 512 M 內(nèi)存的 Android 設(shè)備下所有操作過(guò)程中的卡頓感是否能接受,不會(huì)感覺(jué)突兀怪異愉舔;
4.3 渲染性能差的根源
????????當(dāng)你看到藍(lán)色的線較高的時(shí)候钢猛,可能是由于你的視圖突然無(wú)效了需要重新繪制,或者是自定義的視圖過(guò)于復(fù)雜耗時(shí)過(guò)長(zhǎng)轩缤。
????????當(dāng)你看到紅色的線較高的時(shí)候命迈,可能是由于你的視圖重新提交了需要重新繪制導(dǎo)致的(比如屏幕從豎屏旋轉(zhuǎn)成橫屏后當(dāng)前界面重新創(chuàng)建),或者是自定義的視圖很復(fù)雜火的,繪制起來(lái)很麻煩壶愤,導(dǎo)致耗時(shí)過(guò)長(zhǎng)。比如下面這種視圖:
????????當(dāng)你看到黃色的線較高的時(shí)候馏鹤,那就意味著你給 GPU 太多的工作征椒,太多的負(fù)責(zé)視圖需要 OpenGL 命令去繪制和處理,導(dǎo)致 CPU 遲遲沒(méi)等到 GPU 發(fā)出接到命令的回復(fù)湃累。
4.4 檢測(cè)說(shuō)明
????????這個(gè)工具能夠很好地幫助你找到渲染相關(guān)的問(wèn)題勃救,幫助你找到卡頓的性能瓶頸,追蹤究竟是什么導(dǎo)致被測(cè)應(yīng)用出現(xiàn)卡頓治力、變慢的情況蒙秒,以便在代碼層面進(jìn)行優(yōu)化。甚至讓負(fù)責(zé)產(chǎn)品設(shè)計(jì)的人去改善他的設(shè)計(jì)宵统,以獲得良好的用戶體驗(yàn)晕讲。
????????檢測(cè)渲染性能時(shí),常伴隨著開啟“ 嚴(yán)格模式 ” 查看應(yīng)用哪些情景在 UI 線程(主線程)上執(zhí)行時(shí)間過(guò)長(zhǎng)马澈。
????????另外有些強(qiáng)大但可能少用的工具在測(cè)試性能渲染時(shí)輔助分析瓢省,比如:
HierarchyViewer:這個(gè)工具常用來(lái)查看界面的視圖結(jié)構(gòu)是否過(guò)于復(fù)雜,用于了解哪些視圖過(guò)度繪制箭券,又該如何進(jìn)行改進(jìn);
Tracer for OpenGL:這個(gè)工具收集了所有UI界面發(fā)給GPU的繪制命令疑枯。常用于輔助開發(fā)人員 DEBUG 辩块、定位一些 HierarchyViewer 工具定位不了的疑難渲染細(xì)節(jié)問(wèn)題。
4.5 UI繪制機(jī)制的補(bǔ)充說(shuō)明
????????如上面所說(shuō),布局和 UI 組件等都會(huì)先經(jīng)過(guò) 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í)(比如不可見(jiàn)了)粗恢,那之間的 DisplayList 就無(wú)法繼續(xù)使用了柑晒,這時(shí)系統(tǒng)就會(huì)重新執(zhí)行一次重新創(chuàng)建 DisplayList 、渲染DisplayList 并更新到屏幕上眷射。這個(gè)流程的表現(xiàn)性能取決于該視圖的復(fù)雜程度匙赞。
五. 布局邊界合理性
????????這一章節(jié)比較簡(jiǎn)單,考慮到應(yīng)該沒(méi)受眾妖碉,故不展開討論涌庭。
六. 給開發(fā)的界面優(yōu)化 Advice
6.1 優(yōu)化布局的結(jié)構(gòu)
????????布局結(jié)構(gòu)太復(fù)雜,會(huì)減慢渲染的速度欧宜,造成性能瓶頸坐榆。我們可以通過(guò)以下這些慣用、有效的布局原則來(lái)優(yōu)化:
-
避免復(fù)雜的View層級(jí)鱼鸠。布局越復(fù)雜就越臃腫猛拴,就越容易出現(xiàn)性能問(wèn)題,尋找最節(jié)省資源的方式去展示嵌套的內(nèi)容蚀狰;
-
盡量避免在視圖層級(jí)的頂層使用相對(duì)布局?RelativeLayout?愉昆。相對(duì)布局?RelativeLayout?比較耗資源,因?yàn)橐粋€(gè)相對(duì)布局?RelativeLayout?需要兩次度量來(lái)確保自己處理了所有的布局關(guān)系麻蹋,而且這個(gè)問(wèn)題會(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)系堪侯,可以避免兩次度量的問(wèn)題;
-
相對(duì)復(fù)雜的布局建議采用相對(duì)布局?RelativeLayout?荔仁,相對(duì)布局?RelativeLayout?可以簡(jiǎn)單實(shí)現(xiàn)線性布局?LinearLayout?嵌套才能實(shí)現(xiàn)的布局伍宦;
-
不要使用絕對(duì)布局?AbsoluteLayout?芽死;
-
將可重復(fù)使用的組件抽取出來(lái)并用?標(biāo)簽進(jìn)行重用。如果應(yīng)用多個(gè)地方的 UI 用到某個(gè)布局次洼,就將其寫成一個(gè)布局部件关贵,便于各個(gè) UI 重用。官方詳解 「?戳我?」
-
使用?merge?標(biāo)簽減少布局的嵌套層次卖毁,官方詳解 「?戳我?」揖曾;
-
去掉多余的不可見(jiàn)背景。有多層背景顏色的布局亥啦,只留最上層的對(duì)用戶可見(jiàn)的顏色即可炭剪,其他用戶不可見(jiàn)的底層顏色可以去掉,減少無(wú)效的繪制操作禁悠;
-
盡量避免使用 layoutweight 屬性念祭。使用包含 layoutweight 屬性的線性布局?LinearLayout?每一個(gè)子組件都需要被測(cè)量?jī)纱危瑫?huì)消耗過(guò)多的系統(tǒng)資源碍侦。在使用?ListView?標(biāo)簽與?GridView?標(biāo)簽的時(shí)候粱坤,這個(gè)問(wèn)題顯的尤其重要,因?yàn)樽咏M件會(huì)重復(fù)被創(chuàng)建瓷产。平分布局可以使用相對(duì)布局?RelativeLayout?里一個(gè) 0dp 的 view 做分割線來(lái)搞定站玄,如果不行,那就……濒旦;
合理的界面的布局結(jié)構(gòu)應(yīng)是寬而淺株旷,而不是窄而深;
6.2 優(yōu)化處理邏輯
-
按需載入視圖尔邓。某些不怎么重用的耗資源視圖晾剖,可以等到需要的時(shí)候再加載,提高UI渲染速度梯嗽;
-
使用?ViewStub?標(biāo)簽來(lái)加載一些不常用的布局齿尽;
-
動(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ī)制。我們常用廣播來(lái)接收那些期望響應(yīng)的消息和事件苞慢,但過(guò)多的響應(yīng)超過(guò)本身需求的話诵原,會(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)存分配情況鸵赖;
來(lái)源:http://www.apkbus.com/blog-873057-72671.html