Android界面性能調(diào)優(yōu)手冊(cè)

界面是 Android 應(yīng)用中直接影響用戶體驗(yàn)最關(guān)鍵的部分。如果代碼實(shí)現(xiàn)得不好年鸳,界面容易發(fā)生卡頓且導(dǎo)致應(yīng)用占用大量?jī)?nèi)存晌畅。

我司這類(lè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ō)明

五. 布局邊界合理性

六. 給開(kāi)發(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)畫(huà)和輸入事件的過(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à)面也就不會(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)畫(huà)重復(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的畫(huà)面更新。

市面上絕大多數(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)足夠支撐大部分電影畫(huà)面所要表達(dá)的內(nèi)容债鸡,同時(shí)能最大限度地減少費(fèi)用支出江滨。但是,低于 30 Fps 是無(wú)法順暢表現(xiàn)絢麗的畫(huà)面內(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ī)制擒悬,常稱(chēng) GC 。頻繁的 GC 也是導(dǎo)致嚴(yán)重性能問(wèn)題的罪魁禍?zhǔn)字弧?/p>

前面提到惹挟,平滑的完成一幀意味著所有渲染代碼都必須在 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à)的情況下,可能會(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)畫(huà)仍然在 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度再拍另外一張照片,將兩照片的中間剪開(kāi)并拼接在一起告希,得到下圖:

中間這部分有明顯區(qū)別的部分扑浸,等價(jià)于設(shè)備刷新率和幀速率不一致的結(jié)果。

一般而言燕偶, GPU 的幀速率應(yīng)高于刷新率喝噪,才不會(huì)卡頓或掉幀。如果屏幕刷新率比幀速率還快杭跪,屏幕會(huì)在兩幀中顯示同一個(gè)畫(huà)面仙逻,這種斷斷續(xù)續(xù)情況持續(xù)發(fā)生時(shí),用戶將會(huì)很明顯地感覺(jué)到動(dòng)畫(huà)的卡頓或者掉幀涧尿,然后又恢復(fù)正常系奉,我們常稱(chēng)之為閃屏、跳幀姑廉、延遲缺亮。

應(yīng)用應(yīng)避免這些幀率下降的情況,以確保 GPU 能在屏幕刷新之前完成數(shù)據(jù)的獲取及寫(xiě)入桥言,保證動(dòng)畫(huà)流暢萌踱。

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)畫(huà)的顯示更加復(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è)元素顯示的合理性)

還有專(zhuān)項(xiàng)測(cè)試中某些用戶場(chǎng)景可能還包含著另外一些隱形的檢測(cè)維度顶猜,比如:

OpenGL 跟蹤分析;

GPU 視圖更新合理性痘括;

Flash 硬件層更新合理性长窄;

動(dòng)畫(huà)加 / 減速狀態(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)題肆资,以讓?xiě)?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)簽;

……

更多 Lint 的官方介紹「戳我」抗愁。

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 的開(kāi)發(fā)者選項(xiàng)里打開(kāi) “ 調(diào)試 GPU 過(guò)度繪制 ” 养泡,來(lái)查看應(yīng)用所有界面及分支界面下的過(guò)度繪制情況嗜湃,方便進(jìn)行優(yōu)化。官方介紹 「戳我」澜掩。

2.2.6 GPU 呈現(xiàn)模式分析

通過(guò)在 Android 設(shè)備的設(shè)置 APP 的開(kāi)發(fā)者選項(xiàng)里啟動(dòng) “ GPU 呈現(xiàn)模式分析 ” 购披,可以得到最近 128 幀 每一幀渲染的時(shí)間,分析性能渲染的性能及性能瓶頸肩榕。官方介紹 「戳我」刚陡。

2.2.7 StrictMode

通過(guò)在 Android 設(shè)備的設(shè)置 APP 的開(kāi)發(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 的開(kāi)發(fā)者選項(xiàng)里打開(kāi) “ 窗口動(dòng)畫(huà)縮放 ” / “ 過(guò)渡動(dòng)畫(huà)縮放 ” / “ 動(dòng)畫(huà)程序時(shí)長(zhǎng)縮放 ”乔妈,來(lái)加速或減慢動(dòng)畫(huà)的時(shí)間蝙云,以查看加速或減慢狀態(tài)下的動(dòng)畫(huà)是否會(huì)有問(wèn)題。

2.2.9 Show hardware layer updates

通過(guò)在 Android 設(shè)備的設(shè)置 APP 的開(kāi)發(fā)者選項(xiàng)里啟動(dòng) “ 顯示硬件層更新 ”路召,當(dāng) Flash 硬件層在進(jìn)行更新時(shí)會(huì)顯示為綠色勃刨。使用這個(gè)工具可以讓你查看在動(dòng)畫(huà)期間哪些不期望更新的布局有更新,方便你進(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)具體講述如何解決搞监,以及給開(kāi)發(fā)的優(yōu)化建議。

三. 界面過(guò)度繪制(OverDraw)

3.1 過(guò)度繪制概念

過(guò)度繪制是一個(gè)術(shù)語(yǔ)镰矿,表示某些組件在屏幕上的一個(gè)像素點(diǎn)的繪制次數(shù)超過(guò) 1 次琐驴。

通俗來(lái)講,繪制界面可以類(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 的開(kāi)發(fā)者選項(xiàng)里打開(kāi) “ 調(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)樹(shù)的深度越深,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 ,有興趣的可以去研究查看律罢。

C-like

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 overdrawmRenderState.stencil().enableDebugTest(2);drawColor(mCaches.getOverdrawColor(1),SkXfermode::kSrcOver_Mode);// 2x overdrawmRenderState.stencil().enableDebugTest(3);drawColor(mCaches.getOverdrawColor(2),SkXfermode::kSrcOver_Mode);// 3x overdrawmRenderState.stencil().enableDebugTest(4);drawColor(mCaches.getOverdrawColor(3),SkXfermode::kSrcOver_Mode);// 4x overdraw and highermRenderState.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 的開(kāi)發(fā)者選項(xiàng)里打開(kāi) “ 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è)小柱狀圖代表的這一幀畫(huà)面渲染的耗時(shí)荐捻,柱狀圖越高代表耗時(shí)越長(zhǎng)黍少。隨著界面的刷新寡夹,柱狀圖信息也會(huì)實(shí)時(shí)滾動(dòng)刷新。

中間有一條綠線厂置,代表 16 ms 菩掏,保持動(dòng)畫(huà)流暢的關(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í)愉耙,常伴隨著開(kāi)啟“ 嚴(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的繪制命令赌渣。常用于輔助開(kāi)發(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)該視圖有類(lèi)似位置被移動(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)受眾鞠呈,故不展開(kāi)討論融师。

六. 給開(kā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è)布局域那,就將其寫(xiě)成一個(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)存分配情況泼舱;


https://androidtest.org/android-graphics-performance-pattens/

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市枷莉,隨后出現(xiàn)的幾起案子娇昙,更是在濱河造成了極大的恐慌,老刑警劉巖笤妙,帶你破解...
    沈念sama閱讀 206,126評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件冒掌,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡蹲盘,警方通過(guò)查閱死者的電腦和手機(jī)股毫,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)召衔,“玉大人铃诬,你說(shuō)我怎么就攤上這事〔粤荩” “怎么了趣席?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,445評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)醇蝴。 經(jīng)常有香客問(wèn)我宣肚,道長(zhǎng),這世上最難降的妖魔是什么悠栓? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,185評(píng)論 1 278
  • 正文 為了忘掉前任霉涨,我火速辦了婚禮,結(jié)果婚禮上惭适,老公的妹妹穿的比我還像新娘笙瑟。我一直安慰自己,他們只是感情好腥沽,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布逮走。 她就那樣靜靜地躺著,像睡著了一般今阳。 火紅的嫁衣襯著肌膚如雪师溅。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 48,970評(píng)論 1 284
  • 那天盾舌,我揣著相機(jī)與錄音墓臭,去河邊找鬼。 笑死妖谴,一個(gè)胖子當(dāng)著我的面吹牛窿锉,可吹牛的內(nèi)容都是我干的酌摇。 我是一名探鬼主播,決...
    沈念sama閱讀 38,276評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼嗡载,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼窑多!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起洼滚,我...
    開(kāi)封第一講書(shū)人閱讀 36,927評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤埂息,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后遥巴,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體千康,經(jīng)...
    沈念sama閱讀 43,400評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評(píng)論 2 323
  • 正文 我和宋清朗相戀三年铲掐,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了拾弃。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 37,997評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡摆霉,死狀恐怖豪椿,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情斯入,我是刑警寧澤砂碉,帶...
    沈念sama閱讀 33,646評(píng)論 4 322
  • 正文 年R本政府宣布,位于F島的核電站刻两,受9級(jí)特大地震影響增蹭,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜磅摹,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評(píng)論 3 307
  • 文/蒙蒙 一滋迈、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧户誓,春花似錦饼灿、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,204評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至悼潭,卻和暖如春庇忌,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背舰褪。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,423評(píng)論 1 260
  • 我被黑心中介騙來(lái)泰國(guó)打工皆疹, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人占拍。 一個(gè)月前我還...
    沈念sama閱讀 45,423評(píng)論 2 352
  • 正文 我出身青樓略就,卻偏偏與公主長(zhǎng)得像捎迫,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子表牢,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評(píng)論 2 345

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