1. 程式人生 > 實用技巧 >android效能優化(一)渲染

android效能優化(一)渲染

>>> hot3.png

一 相關理論

1.卡頓原因

大多數使用者感知到的卡頓等效能問題的最主要根源都是因為渲染效能。

2.60fps

12fps大概類似手動快速翻動書籍的幀率,這明顯是可以感知到不夠順滑的。24fps使得人眼感知的是連續線性的運動,這其實是歸功於運動模糊的效果。24fps是電影膠圈通常使用的幀率,因為這個幀率已經足夠支撐大部分電影畫面需要表達的內容,同時能夠最大的減少費用支出。但是低於30fps是無法順暢表現絢麗的畫面內容的,此時就需要用到60fps來達到想要的效果。這意味著每一幀你只有16ms=1000/60的時間來處理所有的任務。Android系統每隔16ms發出VSYNC訊號,觸發對UI進行渲染,如果每次渲染都成功,這樣就能夠達到流暢的畫面所需要的60fps,為了能夠實現60fps,這意味著程式的大多數操作都必須在16ms內完成。

161020_soIt_2832792.png

如果你的某個操作花費時間是24ms,系統在得到VSYNC訊號的時候就無法進行正常渲染,這樣就發生了丟幀現象。

161239_r3YT_2832792.png

使用者容易在UI執行動畫或者滑動ListView的時候感知到卡頓不流暢,是因為這裡的操作相對複雜,容易發生丟幀的現象,從而感覺卡頓。有很多原因可以導致丟幀,也許是因為你的layout太過複雜,無法在16ms內完成渲染,有可能是因為你的UI上有層疊太多的繪製單元,還有可能是因為動畫執行的次數過多。這些都會導致CPU或者GPU負載過重。

我們可以通過一些工具來定位問題,比如可以使用HierarchyViewer來查詢Activity中的佈局是否過於複雜,也可以使用手機設定裡面的開發者選項,開啟Show GPU Overdraw等選項進行觀察。你還可以使用TraceView來觀察CPU的執行情況,更加快捷的找到效能瓶頸。

3.渲染操作

xml佈局如何繪製到螢幕上?

Resterization柵格化是繪製那些Button,Shape,Path,String,Bitmap等元件最基礎的操作。它把那些元件拆分到不同的畫素上進行顯示。這是一個很費時的操作,GPU的引入就是為了加快柵格化的操作。

162523_4JiW_2832792.png

CPU負責把XML佈局元件計算成Polygons,Texture紋理,然後交給GPU進行柵格化處理,最後交由螢幕顯示。

162703_Eo9k_2832792.png

二 解決方案

1.效能優化一:過度繪製

164059_GSI5_2832792.png

Overdraw(過度繪製)描述的是螢幕上的某個畫素在同一幀的時間內被繪製了多次。在多層次的UI結構裡面,如果不可見的UI也在做繪製的操作,這就會導致某些畫素區域被繪製了多次。這就浪費大量的CPU以及GPU資源。為了解決這個問題,Android系統會通過避免繪製那些完全不可見的元件來儘量減少Overdraw。那些不可見的View就不會被執行浪費資源。

那些過於複雜的自定義的View(重寫了onDraw方法),Android系統無法檢測具體在onDraw裡面會執行什麼操作,系統無法監控並自動優化,也就無法避免Overdraw了。但是我們可以通過canvas.clipRect()來幫助系統識別那些可見的區域。這個方法可以指定一塊矩形區域,只有在這個區域內才會被繪製,其他的區域會被忽視。這個API可以很好的幫助那些有多組重疊元件的自定義View來控制顯示的區域。同時clipRect方法還可以幫助節約CPU與GPU資源,在clipRect區域之外的繪製指令都不會被執行,那些部分內容在矩形區域內的元件,仍然會得到繪製。除了clipRect方法之外,我們還可以使用canvas.quickreject()來判斷是否沒和某個矩形相交,從而跳過那些非矩形區域內的繪製操作。

Overdraw有時候是因為你的UI佈局存在大量重疊的部分,還有的時候是因為非必須的重疊背景。例如某個Activity有一個背景,然後裡面的Layout又有自己的背景,同時子View又分別有自己的背景。僅僅是通過移除非必須的背景圖片,這就能夠減少大量的紅色Overdraw區域,增加藍色區域的佔比。這一措施能夠顯著提升程式效能。

我們可以通過手機設定裡面的開發者選項,開啟Show GPU Overdraw的選項,可以觀察UI上的Overdraw情況。

164529_5Vls_2832792.png

2.效能優化二:減少佈局變動導致的渲染

Android需要把XML佈局檔案轉換成GPU能夠識別並繪製的物件。這個操作是在DisplayList的幫助下完成的。DisplayList持有所有將要交給GPU繪製到螢幕上的資料資訊。在某個View第一次需要被渲染時,DisplayList會因此而被建立,當這個View要顯示到螢幕上時,我們會執行GPU的繪製指令來進行渲染。如果你修改了View中的某些可見元件,那麼之前的DisplayList就無法繼續使用了,我們需要回頭重新建立一個DisplayList並且重新執行渲染指令並更新到螢幕上。

假設某個Button的大小需要增大到目前的兩倍,在增大Button大小之前,需要通過父View重新計算並擺放其他子View的位置。修改View的大小會觸發整個HierarcyView的重新計算大小的操作。如果是修改View的位置則會觸發HierarchView重新計算其他View的位置。如果佈局很複雜,這就會很容易導致嚴重的效能問題。我們需要儘量減少Overdraw。我們需要減少View層級巢狀,並且view的大小盡量用固定尺寸,少用自適應。

參考:http://hukai.me/android-performance-patterns/

轉載於:https://my.oschina.net/kun123/blog/967783