深入Android渲染機制


1.知識儲備

  • CPU: 中央處理器,它集成了運算,緩沖,控制等單元,包括繪圖功能.CPU將對象處理為多維圖形,紋理(Bitmaps、Drawables等都是一起打包到統一的紋理).

  • GPU:一個類似於CPU的專門用來處理Graphics的處理器, 作用用來幫助加快格柵化操作,當然,也有相應的緩存數據(例如緩存已經光柵化過的bitmap等)機制。

  • OpenGL ES是手持嵌入式設備的3DAPI,跨平台的、功能完善的2D和3D圖形應用程序接口API,有一套固定渲染管線流程. 附相關OpenGL渲染流程資料

  • DisplayListAndroid把XML布局文件轉換成GPU能夠識別並繪制的對象。這個操作是在DisplayList的幫助下完成的。DisplayList持有所有將要交給GPU繪制到屏幕上的數據信息。

  • 格柵化 是 將圖片等矢量資源,轉化為一格格像素點的像素圖,顯示到屏幕上,過程圖如下.

格柵化操作

  • 垂直同步VSYNC:讓顯卡的運算和顯示器刷新率一致以穩定輸出的畫面質量。它告知GPU在載入新幀之前,要等待屏幕繪制完成前一幀。下面的三張圖分別是GPU和硬件同步所發生的情況,Refresh Rate:屏幕一秒內刷新屏幕的次數,由硬件決定,例如60Hz.而Frame Rate:GPU一秒繪制操作的幀數,單位是30fps,正常情況過程圖如下.

正常情況


2.渲染機制分析

渲染流程線

UI對象—->CPU處理為多維圖形,紋理 —–通過OpeGL ES接口調用GPU—-> GPU對圖進行光柵化(Frame Rate ) —->硬件時鍾(Refresh Rate)—-垂直同步—->投射到屏幕

圖片名稱

渲染時間線

Android系統每隔16ms發出VSYNC信號(1000ms/60=16.66ms),觸發對UI進行渲染, 如果每次渲染都成功,這樣就能夠達到流暢的畫面所需要的60fps,為了能夠實現60fps,這意味着計算渲染的大多數操作都必須在16ms內完成。

正常情況

這里寫圖片描述

渲染超時,計算渲染時間超過16ms

當這一幀畫面渲染時間超過16ms的時候,垂直同步機制會讓顯示器硬件 等待GPU完成柵格化渲染操作,
這樣會讓這一幀畫面,多停留了16ms,甚至更多.這樣就這造成了 用戶看起來 畫面停頓.

當GPU渲染速度過慢,就會導致如下情況,某些幀顯示的畫面內容就會與上一幀的畫面相同

GPU超時情況


3.渲染時會出現的問題

GPU過度繪制

GPU的繪制過程,就跟刷牆一樣,一層層的進行,16ms刷一次.這樣就會造成,圖層覆蓋的現象,即無用的圖層還被繪制在底層,造成不必要的浪費.

這里寫圖片描述

過度繪制查看工具

在手機端的開發者選項里,有OverDraw監測工具,調試GPU過度繪制工具,
其中顏色代表渲染的圖層情況,分別代表1層,2層,3層,4層覆蓋.

這里寫圖片描述

我的魅族手機的Monitor GPU Rendering

這里寫圖片描述

計算渲染的耗時

任何時候View中的繪制內容發生變化時,都會重新執行創建DisplayList,渲染DisplayList,更新到屏幕上等一 系列操作。這個流程的表現性能取決於你的View的復雜程度,View的狀態變化以及渲染管道的執行性能。

舉個例子,當View的大小發生改變,DisplayList就會重新創建,然后再渲染,而當View發生位移,則DisplayList不會重新創建,而是執行重新渲染的操作.

當你的View過於復雜,操作又過於復雜,就會計算渲染時間超過16ms,產生卡頓問題.

渲染耗時呈現工具

工具中,不同手機呈現方式可能會有差別.分別關於StatusBar,NavBar,激活的程序Activity區域的GPU Rending信息。激活的程序Activity區域的GPU Rending信息。

界面上會滾動顯示垂直的柱狀圖來表示每幀畫面所需要渲染的時間,柱狀圖越高表示花費的渲染時間越長。

中間有一根綠色的橫線,代表16ms,我們需要確保每一幀花費的總時間都低於這條橫線,這樣才能夠避免出現卡頓的問題。

這里寫圖片描述

每一條柱狀線都包含三部分,
藍色代表測量繪制Display List的時間,
紅色代表OpenGL渲染Display List所需要的時間,
黃色代表CPU等待GPU處理的時間。

這里寫圖片描述


4. 如何優化

有人會說這些小地方,不值得優化.但是當你用的是低配機器,內存到飽和,CPU運算到達飽和,就像一個界面要做很多交互,繪制,加載圖片,請求網絡.后,一個小問題就會導致頁面卡頓(就像我手機的淘寶客戶端…),OOM,項目崩潰.

是的,這就是力量~

Android系統已經對它優化

在Android里面那些由主題所提供的資源,例如Bitmaps,Drawables都是一起打包到統一的Texture紋理當中,然后再傳遞到 GPU里面,這意味着每次你需要使用這些資源的時候,都是直接從紋理里面進行獲取渲染的。

我們要做的優化

扁平化處理,防止過度繪制OverDraw

1.每一個layout的最外層父容器 是否需要?

這里寫圖片描述

2.布局層級優化

進行檢測時,可能會讓多種檢測工具沖突,用Android Device Monitor的時候,最好關閉相關手機上的開發者檢測工具開關.
查看自己的布局,深的層級,是否可以做優化.
渲染比較耗時(顏色就能看出來),想辦法能否減少層級以及優化每一個View的渲染時間.

Hierarchy Viewer工具

他是查看耗時情況,和布局樹的深度的工具.

這里寫圖片描述

3.圖片選擇

Android的界面能用png最好是用png了,因為32位的png顏色過渡平滑且支持透明。jpg是像素化壓縮過的圖片,質量已經下降了,再拿來做9path的按鈕和平鋪拉伸的控件必然慘不忍睹,要盡量避免。

對於顏色繁雜的,照片牆紙之類的圖片(應用的啟動畫面喜歡搞這種),那用jpg是最好不過了,這種圖片壓縮前壓縮后肉眼分辨幾乎不計,如果保存成png體積將是jpg的幾倍甚至幾十倍,嚴重浪費體積。

4.清理不必要的背景

這里寫圖片描述

5.當背景無法避免,盡量用Color.TRANSPARENT

因為透明色Color.TRANSPARENT是不會被渲染的,他是透明的.



//優化前 //優化前: 當圖片不為空,ImageView加載圖片,然后統一設置背景 Bean bean=list.get(i); if (bean.img == 0) { Picasso.with(getContext()).load(bean.img).into(holder.imageView); } chat_author_avatar.setBackgroundColor(bean.backPic); 

 

//優化后 //優化后:當圖片不為空,ImageView加載圖片,並設置背景為TRANSPARENT; //當圖片為空,ImageView加載TRANSPARENT,然后設置背景為無照片背景 Bean bean=list.get(i); if (bean.img == 0) { Picasso.with(getContext()).load(android.R.color.transparent).into(holder.imageView); holder.imageView.setBackgroundColor(bean.backPic); } else { Picasso.with(getContext()).load(bean.img).into(holder.imageView); holder.imageView.setBackgroundColor(Color.TRANSPARENT); } 

 

————-對比結果——————–
這里寫圖片描述

6.優化自定義View的計算

View中的方法OnMeasure,OnLayout,OnDraw.在我們自定義View起到了決定作用,我們要學會研究其中的優化方法.

學會裁剪掉View的覆蓋部分,增加cpu的計算量,來優化GPU的渲染


  /** * Intersect the current clip with the specified rectangle, which is * expressed in local coordinates. * * @param left The left side of the rectangle to intersect with the * current clip * @param top The top of the rectangle to intersect with the current clip * @param right The right side of the rectangle to intersect with the * current clip * @param bottom The bottom of the rectangle to intersect with the current * clip * @return true if the resulting clip is non-empty */ public boolean clipRect(float left, float top, float right, float bottom) { return native_clipRect(mNativeCanvasWrapper, left, top, right, bottom, Region.Op.INTERSECT.nativeInt); } 

 


5.總結

性能優化其實不僅僅是一種技術,而是一種思想,你只聽過它的高大上,卻不知道它其實就是各個細節處的深入研究和處理.

當然,有的時候也需要自己進行權衡效果和性能,根據需求進行選擇.

還有,Android Device Monitor 是個好東西~簡直就是性能優化大本營,性能優化的工具基本都在其中.

所以在平時的開發過程中,養成良好的思考習慣,是第一步~

寫代碼的時候要想:
1.你的代碼是不是多余?
2.你的對象有沒有必要在循環中創建?
3.你的計算方法是不是最優?

畫界面的時候要想:
1.布局是否有背景?
2.是否可以刪掉多余的布局?
3.自定義View是否進行了裁剪處理?
4.布局是否扁平化,移除非必需的UI組?

最后,Android Device Monitor 是個好東西~ 性能優化的工具基本都在其中.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM