總結(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的性能無法滿足上圖的條件,又是什么情況呢叮雳?
這個圖采用了雙緩沖说莫,以及前面介紹的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),他們有著不同的層級哀蘑。
下圖顯示了使用2種不同的寫法诚卸,在Hierarchy Viewer上呈現(xiàn)出來的性能測試差異:
14)Optimizing Your Layout
下圖舉例演示了如何優(yōu)化ListItem的布局,通過RelativeLayout替代舊方案中的嵌套LinearLayout來優(yōu)化布局绘迁。