Android性能優(yōu)化之渲染篇

總結(jié)Android渲染部分的工作原理,其中參考了如下網(wǎng)址:
http://www.androidpolice.com/2012/07/12/getting-to-know-android-4-1-part-3-project-butter-how-it-works-and-what-it-added/
http://blog.csdn.net/michaelcao1980/article/details/43233765
https://en.wikipedia.org/wiki/Screen_tearing
http://hukai.me/android-performance-render/

1)基本概念

在一個典型的顯示系統(tǒng)中,一般包括CPU妇垢、GPU、display三個部分驳遵, CPU負(fù)責(zé)計計算數(shù)據(jù)墩莫,把計算好數(shù)據(jù)交給GPU,GPU會對圖形數(shù)據(jù)進(jìn)行渲染,渲染好后放到buffer里存起來绣张,然后display(有的文章也叫屏幕或者顯示器)負(fù)責(zé)把buffer里的數(shù)據(jù)呈現(xiàn)到屏幕上。很多時候关带,我們可以把CPU侥涵、GPU放在一起說,那么就是包括2部分,CPU/GPU 和display(本文主要按后面這種分類來解釋)芜飘。
tearing: 一個屏幕內(nèi)的數(shù)據(jù)來自2個不同的幀务豺,畫面會出現(xiàn)撕裂感。
jank: 一個幀在屏幕上連續(xù)出現(xiàn)2次嗦明。
lag:從用戶體驗來說笼沥,就是點擊下去到呈現(xiàn)效果之間存在延遲。
Refresh Rate:代表了屏幕在一秒內(nèi)刷新屏幕的次數(shù)娶牌,這取決于硬件的固定參數(shù)奔浅,例如60Hz。
Frame Rate:代表了GPU在一秒內(nèi)繪制操作的幀數(shù)裙戏,例如30fps乘凸,60fps。

2)Screen tearing問題如何解決
screen tearing

顯示過程累榜,簡單的說就是CPU/GPU準(zhǔn)備好數(shù)據(jù)营勤,存入buffer,display去buffer里取數(shù)據(jù)壹罚,然后顯示出來葛作。如果只有一個buffer,那么這個buffer既被GPU寫猖凛,也同時被display讀赂蠢,如果讀的速度跟寫的速度一樣,那可以正常顯示辨泳。如果讀的比寫的快(顯示器刷新頻率略快于CPU/GPU準(zhǔn)備緩存的速度)虱岂,那也沒什么問題。但是如果讀的比寫的慢的話菠红,很可能有buffer里的數(shù)據(jù)沒有被讀取第岖,就被重寫了,這樣相當(dāng)于一部分?jǐn)?shù)據(jù)丟失了试溯,這是不允許的蔑滓。比如display在讀取幀1的過程中(為了顯示幀1),CPU/GPU把幀2寫到了buffer里 遇绞,而display并不知道此時buffer里已經(jīng)是幀2了键袱,那么就會出現(xiàn)display讀的上半部分是幀1,下半部分是2的摹闽, 出現(xiàn)畫面“割裂”蹄咖,這就叫tearing。

double-buffer

tearing發(fā)生的原因是display讀buffer時付鹿,buffer被修改澜汤,那么多一個buffer是不是能解決問題铝量,是的,事實上目前所有的顯示系統(tǒng)都是雙緩存的,單緩存存在于30年前银亲。
雙緩沖技術(shù),基本原理就是采用兩塊buffer纽匙。一塊back buffer用于CPU/GPU后臺繪制务蝠,另一塊framebuffer則用于顯示,當(dāng)back buffer準(zhǔn)備就緒后烛缔,它們才進(jìn)行交換馏段。不可否認(rèn),doublebuffering可以在很大程度上降低screen tearing錯誤践瓷,但是它是萬能的嗎院喜?

一個需要考慮的問題是我們什么時候進(jìn)行兩個緩沖區(qū)的交換呢?假如是back buffer準(zhǔn)備完成一幀數(shù)據(jù)以后就進(jìn)行晕翠,那么如果此時屏幕還沒有完整顯示上一幀內(nèi)容的話喷舀,肯定是會出問題的×苌觯看來只能是等到屏幕處理完一幀數(shù)據(jù)后硫麻,才可以執(zhí)行這一操作了。

我們知道樊卓,一個典型的顯示器有兩個重要特性拿愧,行頻和場頻。行頻(HorizontalScanningFrequency)又稱為“水平掃描頻率”碌尔,是屏幕每秒鐘從左至右掃描的次數(shù); 場頻(Vertical Scanning Frequency)也稱為“垂直掃描頻率”浇辜,是每秒鐘整個屏幕刷新的次數(shù)。由此也可以得出它們的關(guān)系:行頻=場頻*縱坐標(biāo)分辨率唾戚。

當(dāng)掃描完一個屏幕后柳洋,設(shè)備需要重新回到第一行以進(jìn)入下一次的循環(huán),此時有一段時間空隙颈走,稱為VerticalBlanking Interval(VBI)膳灶。大家應(yīng)該能想到了,這個時間點就是我們進(jìn)行緩沖區(qū)交換的最佳時間立由。因為此時屏幕沒有在刷新轧钓,也就避免了交換過程中出現(xiàn)screentearing的狀況。VSync(垂直同步)是VerticalSynchronization的簡寫锐膜,它利用VBI時期出現(xiàn)的vertical sync pulse來保證雙緩沖在最佳時間點才進(jìn)行交換毕箍。

總結(jié)

Screen Tearing出現(xiàn)的原因有兩個:
1,單緩沖情況下道盏,在display的時候draw(也就是所謂的On Display Draw)
2而柑,雙緩沖情況下文捶,在display的時候swap buffer(Flip).
所以:
我們只要使用雙緩沖做Flip(避免了On display Draw),并且做VSync同步,即每次等到VSync階段再做swap,就可以完美解決Screen Tearing問題媒咳。

3)VSYNC的前生今世

VSYNC(Vertical Synchronization)是一個相當(dāng)古老的概念粹排,對于游戲玩家,它有一個更加大名鼎鼎的中文名字—-垂直同步涩澡⊥缍“垂直同步(vsync)”指的是顯卡的輸出幀數(shù)和屏幕的垂直刷新率相同,這完全是一個CRT顯示器上的概念妙同。其實無論是VSYNC還是垂直同步這個名字射富,因為LCD根本就沒有垂直掃描的這種東西,因此這個名字本身已經(jīng)沒有意義粥帚。但是基于歷史的原因胰耗,這個名稱在圖形圖像領(lǐng)域被沿襲下來。在當(dāng)下芒涡,垂直同步的含義我們可以理解為柴灯,使得顯卡生成幀的速度和屏幕刷新的速度的保持一致。舉例來說费尽,如果屏幕的刷新率為60Hz弛槐,那么生成幀的速度就應(yīng)該被固定在1/60 s。

Android中的VSYNC — 黃油計劃

從Android 4.1開始依啰,谷歌致力于解決Android系統(tǒng)中最飽受詬病的一個問題乎串,滑動不如iOS流暢。因谷歌在4.1版本引入了一個重大的改進(jìn)—Project Butter速警,也即是黃油計劃叹誉。Project Butter對Android Display系統(tǒng)進(jìn)行了重構(gòu),引入了三個核心元素闷旧,即VSYNC长豁、Triple Buffer和Choreographer。關(guān)于后面兩個概念我們會在后面開專題講解忙灼,這里我們重點講解VSYNC的作用匠襟。玩過大型PC游戲的玩家都知道,VSYNC最重要的作用是防止出現(xiàn)畫面撕裂(screentearing)该园。所謂畫面撕裂酸舍,就是指一個畫面上出現(xiàn)了兩幀畫面的內(nèi)容,如下圖里初。


為什么會出現(xiàn)這種情況呢啃勉?這種情況一般是因為顯卡輸出幀的速度高于顯示器的刷新速度,導(dǎo)致顯示器并不能及時處理輸出的幀双妨,而最終出現(xiàn)了多個幀的畫面都留在了顯示器上的問題淮阐。這也就是我們所說的畫面撕裂叮阅。

提到垂直同步這里就多提一句,其實我認(rèn)為對于PC上的大型游戲來說泣特,只有配置足夠高浩姥,高到顯卡輸出幀率可以穩(wěn)定的高于顯示器的刷新頻率,才有開啟垂直同步的必要状您。因為只有這個時候及刻,畫面撕裂才會真正成為一個問題。而對于很多情況下主機(jī)性能不足導(dǎo)致游戲輸出幀率低于顯示器的刷新頻率的情況下竞阐,尤其是幀率穩(wěn)定在40~60之間時,開啟垂直同步可能會導(dǎo)致幀率倍數(shù)級的下降(具體原因我們在Graphic架構(gòu)一文中提到過暑劝,當(dāng)幀生成速度不及VSync速度時骆莹,幀率的下降不是平緩的,而且很可能是倍數(shù)級的担猛。當(dāng)然這在android系統(tǒng)上并非嚴(yán)重問題幕垦,因為android上很少有高速的復(fù)雜場景的頻繁切換。事實上傅联,在Android的普通應(yīng)用場景下先改,VSync的使用不僅不會降低幀率,還可以有效解決卡頓問題)蒸走。

4)Jank問題仇奶,triple buffer

如果沒有VSync同步, 繪圖速度大于顯示速度那么會有問題(screen tearing).
如果沒有VSync同步,還有另一個問題,就是反過來繪圖速度過慢的時候.
如圖::


這個圖中有三個元素,Display是顯示屏幕比驻,GPU和CPU負(fù)責(zé)渲染幀數(shù)據(jù)该溯,每個幀以方框表示,并以數(shù)字進(jìn)行編號别惦,如0狈茉、1、2等等掸掸。VSync用于指導(dǎo)雙緩沖區(qū)的交換氯庆。以時間的順序來看下將會發(fā)生的異常:
Step1. Display顯示第0幀數(shù)據(jù),此時CPU和GPU渲染第1幀畫面扰付,而且趕在Display顯示下一幀前完成堤撵。
Step2. 因為渲染及時,Display在第0幀顯示完成后羽莺,也就是第1個VSync后粒督,正常顯示第1幀。
Step3. 由于某些原因禽翼,比如CPU資源被占用屠橄,系統(tǒng)沒有及時地開始處理第2幀族跛,直到第2個VSync快來前才開始處理
Step4. 第2個VSync來時,由于第2幀數(shù)據(jù)還沒有準(zhǔn)備就緒锐墙,顯示的還是第1幀礁哄。這種情況被Android開發(fā)組命名為“Jank”。
Step5. 當(dāng)?shù)?幀數(shù)據(jù)準(zhǔn)備完成后溪北,它并不會馬上被顯示桐绒,而是要等待下一個VSync。所以總的來說之拨,就是屏幕平白無故地多顯示了一次第1幀茉继。原因大家應(yīng)該都看到了,就是CPU沒有及時地開始著手處理第2幀的渲染工作蚀乔,以致“延誤軍機(jī)”烁竭。

其實總結(jié)上面的這個情況之所以發(fā)生,首先的原因就在于第二幀沒有及時的繪制吉挣。那么如何使得第二幀及時被繪制呢派撕?這就是我們在Graphic系統(tǒng)中引入VSYNC的原因,考慮下面這張圖:



如上圖所示睬魂,一旦VSync出現(xiàn)后终吼,立刻就開始執(zhí)行下一幀的繪制工作。這樣就可以大大降低Jank出現(xiàn)的概率氯哮。另外际跪,VSYNC引入后,要求繪制也只能在收到VSYNC消息之后才能進(jìn)行喉钢,因此垫卤,也就杜絕了另外一種極端情況的出現(xiàn)—-CPU(GPU)一直不停的進(jìn)行繪制,幀的生成速度高于屏幕的刷新速度出牧,導(dǎo)致生成的幀不能被顯示穴肘,只能丟棄,這樣就出現(xiàn)了丟幀的情況—-引入VSYNC后舔痕,繪制的速度就和屏幕刷新的速度保持一致了评抚。
大部分的Android顯示設(shè)備刷新率是60Hz,這也就意味著每一幀最多只能有1/60=16ms左右的準(zhǔn)備時間。
假如CPU/GPU的FPS(FramesPer Second)高于這個值伯复,那么這個方案是完美的慨代,顯示效果將很好⌒ト纾可是我們沒有辦法保證所有設(shè)備的硬件配置都能達(dá)到要求侍匙。假如CPU/GPU的性能無法滿足上圖的條件,又是什么情況呢叮雳?

在分析這一問題之前想暗,我們先來看下正常情況下妇汗,采用雙緩沖區(qū)的系統(tǒng)的運(yùn)行情況,如圖:

這個圖采用了雙緩沖说莫,以及前面介紹的VSync杨箭,可以看到整個過程還是相當(dāng)不錯的,雖然CPU/GPU處理所用的時間時短時長储狭,但總的來說都在16ms以內(nèi)互婿,因而不影響顯示效果。A和B分別代表兩個緩沖區(qū)辽狈,它們不斷地交換來正確顯示畫面慈参。

現(xiàn)在我們可以繼續(xù)分析FPS低于屏幕刷新率的情況,如圖所示:

當(dāng)CPU/GPU的處理時間超過16ms時刮萌,第一個VSync到來時驮配,緩沖區(qū)B中的數(shù)據(jù)還沒有準(zhǔn)備好,于是只能繼續(xù)顯示之前A緩沖區(qū)中的內(nèi)容尊勿。而B完成后,又因為缺乏VSync pulse信號畜侦,它只能等待下一個signal的來臨元扔。于是在這一過程中,有一大段時間是被浪費的旋膳。當(dāng)下一個VSync出現(xiàn)時澎语,CPU/GPU馬上執(zhí)行操作,此時它可操作的buffer是A验懊,相應(yīng)的顯示屏對應(yīng)的就是B擅羞。這時看起來就是正常的。只不過由于執(zhí)行時間仍然超過16ms义图,導(dǎo)致下一次應(yīng)該執(zhí)行的緩沖區(qū)交換又被推遲了——如此循環(huán)反復(fù),便出現(xiàn)了越來越多的“Jank”。
那么有沒有規(guī)避的辦法呢舰讹?

很顯然北苟,第一次的Jank看起來是沒有辦法的,除非升級硬件配置來加快FPS怕篷。我們關(guān)注的重點是被CPU/GPU浪費的時間段历筝,怎么才能充分利用起來呢?分析上述的過程廊谓,造成CPU/GPU無事可做的假象是因為當(dāng)前已經(jīng)沒有可用的buffer了梳猪。換句話說,如果增加一個buffer蒸痹,情況會不會好轉(zhuǎn)呢春弥?如圖:

Triple Buffering是MultipleBuffering的一種呛哟,指的是系統(tǒng)使用3個緩沖區(qū)用于顯示工作。我們來逐步分析下這個新機(jī)制是否有效惕稻。首先和預(yù)料中的一致竖共,第一次“Jank”無可厚非。不過讓人欣慰的是俺祠,當(dāng)?shù)谝淮蜼Sync發(fā)生后公给,CPU不用再等待了,它會使用第三個buffer C來進(jìn)行下一幀數(shù)據(jù)的準(zhǔn)備工作蜘渣。雖然對緩沖區(qū)C的處理所需時間同樣超過了16ms淌铐,但這并不影響顯示屏——第2次VSync到來后,它選擇buffer B進(jìn)行顯示;而第3次VSync時蔫缸,它會接著采用C腿准,而不是像double buffering中所看到的情況一樣只能再顯示一遍了。這樣子就有效地降低了系統(tǒng)顯示錯誤的機(jī)率拾碌。
注意:triple Bufferring好像完美解決了連續(xù)jank問題吐葱,但是第一次的jank無法避免,而且還有l(wèi)ag問題校翔,例如緩存區(qū)C的內(nèi)容要16ms以后才能顯示弟跑。

5)用戶為什么會感覺到卡頓
根本原因

大多數(shù)用戶感知到的卡頓等性能問題的最主要根源都是因為渲染性能。從設(shè)計師的角度防症,他們希望App能夠有更多的動畫孟辑,圖片等時尚元素來實現(xiàn)流暢的用戶體驗。但是Android系統(tǒng)很有可能無法及時完成那些復(fù)雜的界面渲染操作蔫敲。Android系統(tǒng)每隔16ms發(fā)出VSYNC信號饲嗽,觸發(fā)對UI進(jìn)行渲染,如果每次渲染都成功奈嘿,這樣就能夠達(dá)到流暢的畫面所需要的60fps貌虾,為了能夠?qū)崿F(xiàn)60fps,這意味著程序的大多數(shù)操作都必須在16ms內(nèi)完成裙犹。



如果你的某個操作花費時間是24ms酝惧,系統(tǒng)在得到VSYNC信號的時候就無法進(jìn)行正常渲染,這樣就發(fā)生了丟幀現(xiàn)象伯诬。那么用戶在32ms內(nèi)看到的會是同一幀畫面晚唇。



用戶容易在UI執(zhí)行動畫或者滑動ListView的時候感知到卡頓不流暢,是因為這里的操作相對復(fù)雜盗似,容易發(fā)生丟幀的現(xiàn)象哩陕,從而感覺卡頓。有很多原因可以導(dǎo)致丟幀,也許是因為你的layout太過復(fù)雜悍及,無法在16ms內(nèi)完成渲染闽瓢,有可能是因為你的UI上有層疊太多的繪制單元,還有可能是因為動畫執(zhí)行的次數(shù)過多心赶。這些都會導(dǎo)致CPU或者GPU負(fù)載過重扣讼。
渲染過程CPU和GPU的分工

渲染操作通常依賴于兩個核心組件:CPU與GPU。CPU負(fù)責(zé)包括Measure缨叫,Layout椭符,Record,Execute的計算操作耻姥,GPU負(fù)責(zé)Rasterization(柵格化)操作销钝。CPU通常存在的問題的原因是存在非必需的視圖組件,它不僅僅會帶來重復(fù)的計算操作琐簇,而且還會占用額外的GPU資源蒸健。


了解Android是如何利用GPU進(jìn)行畫面渲染有助于我們更好的理解性能問題。一個很直接的問題是:activity的畫面是如何繪制到屏幕上的婉商?那些復(fù)雜的XML布局文件又是如何能夠被識別并繪制出來的似忧?


Resterization柵格化是繪制那些Button,Shape丈秩,Path盯捌,String,Bitmap等組件最基礎(chǔ)的操作癣籽。它把那些組件拆分到不同的像素上進(jìn)行顯示挽唉。這是一個很費時的操作滤祖,GPU的引入就是為了加快柵格化的操作筷狼。
CPU負(fù)責(zé)把UI組件計算成Polygons,Texture紋理匠童,然后交給GPU進(jìn)行柵格化渲染埂材。

然而每次從CPU轉(zhuǎn)移到GPU是一件很麻煩的事情,所幸的是OpenGL ES可以把那些需要渲染的紋理Hold在GPU Memory里面汤求,在下次需要渲染的時候直接進(jìn)行操作俏险。所以如果你更新了GPU所hold住的紋理內(nèi)容,那么之前保存的狀態(tài)就丟失了扬绪。
在Android里面那些由主題所提供的資源竖独,例如Bitmaps,Drawables都是一起打包到統(tǒng)一的Texture紋理當(dāng)中挤牛,然后再傳遞到GPU里面莹痢,這意味著每次你需要使用這些資源的時候,都是直接從紋理里面進(jìn)行獲取渲染的。當(dāng)然隨著UI組件的越來越豐富竞膳,有了更多演變的形態(tài)航瞭。例如顯示圖片的時候,需要先經(jīng)過CPU的計算加載到內(nèi)存中坦辟,然后傳遞給GPU進(jìn)行渲染刊侯。文字的顯示比較復(fù)雜,需要先經(jīng)過CPU換算成紋理锉走,然后交給GPU進(jìn)行渲染滨彻,返回到CPU繪制單個字符的時候,再重新引用經(jīng)過GPU渲染的內(nèi)容挠日。動畫則存在一個更加復(fù)雜的操作流程疮绷。

為了能夠使得App流暢,我們需要在每幀16ms以內(nèi)處理完所有的CPU與GPU的計算嚣潜,繪制冬骚,渲染等等操作。

后面章節(jié)引言:

前幾章講解的很多display相關(guān)的概念懂算,也描述了Android一些機(jī)制來盡量保證顯示流暢只冻。
double buffering 和VSync解決了 screen tearing 問題。
Triple buffering解決了連續(xù)jank問題计技,但是第一次的jank還是無法避免喜德,而且引入了lag問題,用戶還是會感覺到卡頓垮媒。
假設(shè)硬件不升級的情況下舍悯,如果我們系統(tǒng)能在16ms之內(nèi)完成所有渲染工作,那這些問題都從根本上解決(理想很豐滿睡雇,現(xiàn)實很骨感)萌衬。我們APP編寫者還是要節(jié)約資源,避免一些人為的資源浪費它抱,后面的章節(jié)就從這點入手秕豫,講解App如何避免一些不必要的消耗。

6)Overdraw問題

Overdraw(過度繪制)描述的是屏幕上的某個像素在同一幀的時間內(nèi)被繪制了多次观蓄。在多層次重疊的UI結(jié)構(gòu)里面混移,如果不可見的UI也在做繪制的操作,會導(dǎo)致某些像素區(qū)域被繪制了多次侮穿。這樣就會浪費大量的CPU以及GPU資源歌径。



當(dāng)設(shè)計上追求更華麗的視覺效果的時候,我們就容易陷入采用復(fù)雜的多層次重疊視圖來實現(xiàn)這種視覺效果的怪圈亲茅。這很容易導(dǎo)致大量的性能問題回铛,為了獲得最佳的性能金矛,我們必須盡量減少Overdraw的情況發(fā)生。
幸運(yùn)的是勺届,我們可以通過手機(jī)設(shè)置里面的開發(fā)者選項驶俊,打開Show GPU Overdraw的選項,觀察UI上的Overdraw情況免姿。



藍(lán)色饼酿,淡綠,淡紅胚膊,深紅代表了4種不同程度的Overdraw情況故俐,我們的目標(biāo)就是盡量減少紅色Overdraw,看到更多的藍(lán)色區(qū)域紊婉。
7)Visualize and Fix Overdraw - Quiz & Solution

這里舉了一個例子药版,通過XML文件可以看到有好幾處非必需的background。通過把XML中非必需的background移除之后喻犁,可以顯著減少布局的過度繪制槽片。其中一個比較有意思的地方是:針對ListView中的Avatar ImageView的設(shè)置,在getView的代碼里面肢础,判斷是否獲取到對應(yīng)的Bitmap还栓,在獲取到Avatar的圖像之后,把ImageView的Background設(shè)置為Transparent传轰,只有當(dāng)圖像沒有獲取到的時候才設(shè)置對應(yīng)的Background占位圖片剩盒,這樣可以避免因為給Avatar設(shè)置背景圖而導(dǎo)致的過度渲染。



總結(jié)一下慨蛙,優(yōu)化步驟如下:
移除Window默認(rèn)的Background
移除XML布局文件中非必需的Background
按需顯示占位背景圖片

8)ClipRect & QuickReject

前面有提到過辽聊,對不可見的UI組件進(jìn)行繪制更新會導(dǎo)致Overdraw。例如Nav Drawer從前置可見的Activity滑出之后期贫,如果還繼續(xù)繪制那些在Nav Drawer里面不可見的UI組件跟匆,這就導(dǎo)致了Overdraw。為了解決這個問題唯灵,Android系統(tǒng)會通過避免繪制那些完全不可見的組件來盡量減少Overdraw贾铝。那些Nav Drawer里面不可見的View就不會被執(zhí)行浪費資源隙轻。


但是不幸的是埠帕,對于那些過于復(fù)雜的自定義的View(通常重寫了onDraw方法),Android系統(tǒng)無法檢測在onDraw里面具體會執(zhí)行什么操作玖绿,系統(tǒng)無法監(jiān)控并自動優(yōu)化敛瓷,也就無法避免Overdraw了。但是我們可以通過canvas.clipRect()來幫助系統(tǒng)識別那些可見的區(qū)域斑匪。這個方法可以指定一塊矩形區(qū)域呐籽,只有在這個區(qū)域內(nèi)才會被繪制锋勺,其他的區(qū)域會被忽視。這個API可以很好的幫助那些有多組重疊組件的自定義View來控制顯示的區(qū)域狡蝶。同時clipRect方法還可以幫助節(jié)約CPU與GPU資源庶橱,在clipRect區(qū)域之外的繪制指令都不會被執(zhí)行,那些部分內(nèi)容在矩形區(qū)域內(nèi)的組件贪惹,仍然會得到繪制苏章。

除了clipRect方法之外,我們還可以使用canvas.quickreject()來判斷是否沒和某個矩形相交奏瞬,從而跳過那些非矩形區(qū)域內(nèi)的繪制操作枫绅。

9)Apply clipRect and quickReject - Quiz & Solution

上面的示例圖中顯示了一個自定義的View,主要效果是呈現(xiàn)多張重疊的卡片硼端。這個View的onDraw方法如下圖所示:



打開開發(fā)者選項中的顯示過度渲染并淋,可以看到我們這個自定義的View部分區(qū)域存在著過度繪制。那么是什么原因?qū)е逻^度繪制的呢珍昨?


10)Fixing Overdraw with Canvas API

下面的代碼顯示了如何通過clipRect來解決自定義View的過度繪制县耽,提高自定義View的繪制性能:



下面是優(yōu)化過后的效果:


11)Layouts, Invalidations and Perf

Android需要把XML布局文件轉(zhuǎn)換成GPU能夠識別并繪制的對象。這個操作是在DisplayList的幫助下完成的镣典。DisplayList持有所有將要交給GPU繪制到屏幕上的數(shù)據(jù)信息酬诀。
在某個View第一次需要被渲染時,Display List會因此被創(chuàng)建骆撇,當(dāng)這個View要顯示到屏幕上時瞒御,我們會執(zhí)行GPU的繪制指令來進(jìn)行渲染。
如果View的Property屬性發(fā)生了改變(例如移動位置)神郊,我們就僅僅需要Execute Display List就夠了肴裙。


然而如果你修改了View中的某些可見組件的內(nèi)容,那么之前的DisplayList就無法繼續(xù)使用了涌乳,我們需要重新創(chuàng)建一個DisplayList并重新執(zhí)行渲染指令更新到屏幕上蜻懦。

請注意:任何時候View中的繪制內(nèi)容發(fā)生變化時,都會需要重新創(chuàng)建DisplayList夕晓,渲染DisplayList宛乃,更新到屏幕上等一系列操作。這個流程的表現(xiàn)性能取決于你的View的復(fù)雜程度蒸辆,View的狀態(tài)變化以及渲染管道的執(zhí)行性能征炼。舉個例子,假設(shè)某個Button的大小需要增大到目前的兩倍躬贡,在增大Button大小之前谆奥,需要通過父View重新計算并擺放其他子View的位置。修改View的大小會觸發(fā)整個HierarcyView的重新計算大小的操作拂玻。如果是修改View的位置則會觸發(fā)HierarchView重新計算其他View的位置酸些。如果布局很復(fù)雜宰译,這就會很容易導(dǎo)致嚴(yán)重的性能問題。

12)Hierarchy Viewer: Walkthrough

Hierarchy Viewer可以很直接的呈現(xiàn)布局的層次關(guān)系魄懂,視圖組件的各種屬性沿侈。 我們可以通過紅,黃市栗,綠三種不同的顏色來區(qū)分布局的Measure肋坚,Layout,Executive的相對性能表現(xiàn)如何肃廓。

13)Nested Hierarchies and Performance

提升布局性能的關(guān)鍵點是盡量保持布局層級的扁平化智厌,避免出現(xiàn)重復(fù)的嵌套布局。例如下面的例子盲赊,有2行顯示相同內(nèi)容的視圖铣鹏,分別用兩種不同的寫法來實現(xiàn),他們有著不同的層級哀蘑。



image

下圖顯示了使用2種不同的寫法诚卸,在Hierarchy Viewer上呈現(xiàn)出來的性能測試差異:


14)Optimizing Your Layout

下圖舉例演示了如何優(yōu)化ListItem的布局,通過RelativeLayout替代舊方案中的嵌套LinearLayout來優(yōu)化布局绘迁。


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末合溺,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子缀台,更是在濱河造成了極大的恐慌棠赛,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件膛腐,死亡現(xiàn)場離奇詭異睛约,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)哲身,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進(jìn)店門辩涝,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人勘天,你說我怎么就攤上這事怔揩。” “怎么了脯丝?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵商膊,是天一觀的道長。 經(jīng)常有香客問我巾钉,道長翘狱,這世上最難降的妖魔是什么秘案? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任砰苍,我火速辦了婚禮潦匈,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘赚导。我一直安慰自己茬缩,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布吼旧。 她就那樣靜靜地躺著凰锡,像睡著了一般。 火紅的嫁衣襯著肌膚如雪圈暗。 梳的紋絲不亂的頭發(fā)上掂为,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天,我揣著相機(jī)與錄音员串,去河邊找鬼勇哗。 笑死,一個胖子當(dāng)著我的面吹牛寸齐,可吹牛的內(nèi)容都是我干的欲诺。 我是一名探鬼主播,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼渺鹦,長吁一口氣:“原來是場噩夢啊……” “哼扰法!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起毅厚,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤塞颁,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后吸耿,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體殴边,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年珍语,在試婚紗的時候發(fā)現(xiàn)自己被綠了锤岸。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡板乙,死狀恐怖是偷,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情募逞,我是刑警寧澤蛋铆,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布,位于F島的核電站放接,受9級特大地震影響刺啦,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜纠脾,卻給世界環(huán)境...
    茶點故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一玛瘸、第九天 我趴在偏房一處隱蔽的房頂上張望蜕青。 院中可真熱鬧,春花似錦糊渊、人聲如沸右核。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽贺喝。三九已至,卻和暖如春宗兼,著一層夾襖步出監(jiān)牢的瞬間躏鱼,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工殷绍, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留挠他,地道東北人。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓篡帕,卻偏偏與公主長得像殖侵,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子镰烧,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,901評論 2 345

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