Android效能優化-渲染
你好,我是老七,很高興能夠分享一些我在學習過程中的收穫,本文是學習Google官方渲染UI的學習筆記,如果本文幫助到你了,希望不要吝嗇你的小小喜歡點個心,你的支援是我堅持的動力,接下來就步入正題了,集中你的注意力,效能優化之車要發車了~
效能渲染的定義
Android系統每隔16ms重新繪製一次Activity,也就意味著應用需要在16ms內完成螢幕重新整理的全部邏輯操作,這樣才能達到每秒60幀.
1000ms/60hz = 16.666ms/frame

image.png
這個每秒幀數的引數實際上來源於手機硬體,定義了螢幕每秒重新整理速度有多快,這意味著你有60ms的時間去完成每幀的繪製邏輯操作,如果錯過了,比如你花費了24ms才完成計算,那麼就會出現我們所稱之為丟幀的情況.

image.png
Android系統嘗試在螢幕上繪製新的一幀,但是這一陣還沒有準備好,所以畫面就不會重新整理,就會造成使用者盯著同一張圖看了32ms而不是16ms,丟幀情況下執行的任何動畫就會使使用者很容易察覺出卡頓感,哪怕僅僅出現一次丟幀,使用者都會發現動畫不是很流暢,如果出現多次丟幀,使用者就會開始抱怨卡頓,如果此時使用者正在和系統進行互動操作,例如滑動列表或者輸入資料,那麼卡頓感就會更加明顯,使用者就會開啟吐槽模式~
只有我們對繪製每幀花費的時間有更清晰的瞭解後才能發現是什麼原因導致了卡頓,該如何去解決應用中的這些問題.
渲染管道
Android系統的渲染管道分為兩個關鍵元件:CPU、GPU

image.png
兩者共同工作在螢幕上繪製圖片,每個元件都有自身定義的特定流程,你必須遵守這些特定的操作規則才能達到效果.
常見的效能問題
CPU
最常見的效能問題是不必要的佈局和失效,這些內容必須在師徒層次結構中進行測量、清除並重新建立,而引發這種問題通常有兩個原因,一是重建顯示列表的次數太多,二是花費太多時間作廢檢視層次並進行不必要的重繪.這兩個原因在更新顯示列表,或者其他快取GPU資源時導致CPU工作過度.
GPU
最常見的問題就是過度繪製,通常是在畫素著色過程中,通過其他工具進行後期著色時,浪費了GPU的處理時間
[圖片上傳中...(image.png-d82fb7-1538205529526-0)]
接下來將介紹更多關於失效、佈局和重繪的內容以及如何使用SDK中提供的可用工具找出影響應用效能的原因並且舉例說明如何修復應用中的此類問題.
過度繪製
要想開發一款效能優越的應用,你必須瞭解底層是如何執行的,如果不知道硬體是如何執行就無法熟練使用它.
首先我們要知道:Activity是如何繪製到螢幕上的,那些負責的XML佈局檔案和標記語言是如何轉化成使用者能看懂的影象的?
實際上,這是由光柵化操作來完成的.

image.png
光柵化將注入字串、按鈕、路徑或者形狀的一些高階物件拆分到不同的畫素上在螢幕上進行顯示,並且光柵化是一個非常費時的操作.也就是說你的手機裡有一塊特殊硬體,目的就是加快光柵化的操作,影象處理單元,也就是GPU.
GPU是在上個世紀90年代被引入主流電腦,幫助加快光柵化操作,現在,GPU使用一些指定的基礎指令集,主要是多邊形和紋理,也就是圖片,CPU在螢幕上繪製圖像前會向GPU輸入這些指令,這一過程通常使用的API就是Android的OpenGL ES,這就是說,在螢幕上繪製UI物件時,無論是按鈕、路徑、或者複選框都需要在CPU中首先轉換為多邊形或者紋理,然後再傳遞給GPU進行光柵化.
你可以想象一下,一個UI物件轉換為一系列多邊形和紋理的過程,肯定是相當耗時的,從CPU上傳處理資料到GPU同樣也很耗時,所以很明顯,你需要儘量減少物件轉換的次數以及上傳資料的次數,幸虧OpenGL ES的API允許資料上傳到GPU後可以對資料進行儲存,當你下次繪製一個按鈕時,只需要在GPU儲存器裡引用它,然後告訴OpenGL如何繪製.
到了這裡我們就可以應該可以想到,渲染效能的優化就是儘可能快的上傳資料到GPU,然後儘可能長地在不修改的條件下儲存資料,因為每次上傳資源到GPU時,你都會浪費寶貴的處理時間.
Android系統的Honeycomb(API Level 11)版本釋出之後,整個UI渲染系統就在GPU中執行,之後各個版本都在渲染系統性能方面有更多改進,Android系統在降低、重新利用GPU資源方面做了很多工作,所以在這方面我們完全不用擔心,舉個例子說,任何你的主題所提供的資源,例如Bitmaps、Drawables等都是一起打包到統一的紋理當中,然後利用網格工具上傳到GPU,例如Nine Patches等,這樣每次你需要繪製這些資源時就不用做任何轉換,因為他們已經儲存在GPU中了,大大加快了這些檢視型別的顯示.
然而隨著UI物件的不斷升級,渲染流程也變得越來越複雜,例如說繪製圖像就是把圖片上傳到CPU儲存器,然後傳遞到GPU中進行渲染,路徑使用是完全另一回事,你需要在CPU中建立一系列的多邊形,甚至在GPU中建立掩蔽紋理來定義路徑,繪製字元更加複雜一些,首先我們需要在CPU中把字元繪製成影象,然後把影象上傳到GPU進行渲染再返回到CPU,在螢幕上為字串的每個字元繪製一個正方形.
現在Android系統已經解決了大多數效能問題,除非你還有更高的要求,你基本不會發現與GPU相關的問題,然而還有一個GPU效能問題瓶頸,這個問題困擾著每個程式開發人員,這就是過度繪製.
如果你曾經畫過一個房間或房子,你應該知道在那些牆上塗滿顏色會花費很多功夫,如果你需要重新畫一遍,那你第一次做這件事時就浪費了很多功夫.相同的,浪費精力去繪製某些東西同樣可能會對應用程式中的效能問題產生影響,所以,在效能和設計的交匯處存在一個共同的效能問題-過度繪製.

image.png
過度繪製是一個術語,用來描述螢幕上的一個畫素在一幀中被重畫了多少次,例如,如果我們有一堆層疊的UI,上面的UI層級會遮蓋住底下的UI層級,意味著我們花費很多時間繪製的圖層大部分是不可見的,實際上這是一個很大的問題,因為每次我們渲染的畫素對最終場景沒有幫助,我們就浪費了GPU的效能.使用這樣的佈局,我們很容易陷入一個陷阱.分層的檢視給了我們這個美麗的,卓越的設計,但同樣也導致了過渡繪製的問題.為了最大化應用程式的效能,你需要使用最小化的過渡繪製.

image.png
幸運的是,在Android裝置上很容易看到應用程式中過度繪製的數量.進入手機的開發者模式,開啟GPU過渡繪製的功能,你的手機介面可能會產生視覺上的一些變化,因為Android使用不同的顏色高亮顯示過渡繪製的區域,如果你只在某個畫素上繪製了一次,那麼將不會有任何顏色,然而,隨著過度繪製的增加,顏色也會改變.
依據過度繪製的層度可以分成:
- 無過度繪製(一個畫素只被繪製了一次)
- 過度繪製x1(一個畫素被繪製了兩次)
- 過度繪製x2(一個畫素被繪製了三次)
- 過度繪製x3(一個畫素被繪製了四次)
-
過度繪製x4+(一個畫素被繪製了五次以上
image.png
優化方案
- 首先,你需要從檢視中刪除對最終呈現的影象沒有幫助背景和繪圖,因為這屬於浪費效能.
- 接下來,你可以定義你知道會隱藏部分檢視的螢幕區域,這有助於降低CPU和GPU開銷
方案演示
上面說了那麼多理論性的內容,光說不練假把式,接下來我們真刀真槍的幹上那麼一干.附上了練習專案的地址,大家可以下載下來也動手試試.
ofollow,noindex">練習專案
現在我們開啟應用可以看見這些紅色的過渡繪製的區域,我們的任務就是減少這些過度繪製.

image.png
按照文中之前說的,我們需要先去了解一下UI是如何建立的並且試著做一些清理減少過渡繪製,試著清除不必要的背景和圖片.
分析發現,我們的整體背景現在是藍色過度繪製級別的,而導致過度繪製的原因是在ChatumLatinumActivity中使用了不透明白色背景的佈局填充了整個螢幕,而Android的主題會預設設定顏色.因此就導致了不必要的過度繪製.

image.png
因為我們可能需要自己設定我們應用的背景顏色,所以就需要將主題中的背景顏色取消,我們使用 getWindow().setBackgroundDrawable(null);
來取消原來的背景,這個方法的作用就是去除Window也就是DecorView的背景顏色.效果如下.變成了藍色過度繪製級別.

image.png
接下來就可以仔細的去看看其他的XML檔案是否仍有可以清除的不必要的背景顏色了.
暫時先整理到這.後續會盡快更新.