1.知識(shí)儲(chǔ)備
CPU: 中央處理器,它集成了運(yùn)算,緩沖,控制等單元,包括繪圖功能.CPU將對(duì)象處理為多維圖形,紋理(Bitmaps、Drawables等都是一起打包到統(tǒng)一的紋理).
GPU:一個(gè)類似于CPU的專門用來處理Graphics的處理器, 作用用來幫助加快柵格化操作,當(dāng)然,也有相應(yīng)的緩存數(shù)據(jù)(例如緩存已經(jīng)光柵化過的bitmap等)機(jī)制。
OpenGL ES是手持嵌入式設(shè)備的3DAPI,跨平臺(tái)的、功能完善的2D和3D圖形應(yīng)用程序接口API,有一套固定渲染管線流程. 附相關(guān)OpenGL渲染流程資料
DisplayList 在Android把XML布局文件轉(zhuǎn)換成GPU能夠識(shí)別并繪制的對(duì)象。這個(gè)操作是在DisplayList的幫助下完成的。DisplayList持有所有將要交給GPU繪制到屏幕上的數(shù)據(jù)信息。
柵格化 是 將圖片等矢量資源,轉(zhuǎn)化為一格格像素點(diǎn)的像素圖,顯示到屏幕上,過程圖如下.

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

2.渲染機(jī)制分析
渲染流程線
UI對(duì)象---->CPU處理為多維圖形,紋理 -----通過OpeGL ES接口調(diào)用GPU----> GPU對(duì)圖進(jìn)行光柵化(Frame Rate ) ---->硬件時(shí)鐘(Refresh Rate)----垂直同步---->投射到屏幕
渲染時(shí)間線
Android系統(tǒng)每隔16ms發(fā)出VSYNC信號(hào)(1000ms/60=16.66ms),觸發(fā)對(duì)UI進(jìn)行渲染, 如果每次渲染都成功,這樣就能夠達(dá)到流暢的畫面所需要的60fps,為了能夠?qū)崿F(xiàn)60fps,這意味著計(jì)算渲染的大多數(shù)操作都必須在16ms內(nèi)完成。
正常情況
渲染超時(shí),計(jì)算渲染時(shí)間超過16ms
當(dāng)這一幀畫面渲染時(shí)間超過16ms的時(shí)候,垂直同步機(jī)制會(huì)讓顯示器硬件 等待GPU完成柵格化渲染操作,
這樣會(huì)讓這一幀畫面,多停留了16ms,甚至更多.這樣就這造成了 用戶看起來 畫面停頓.

當(dāng)GPU渲染速度過慢,就會(huì)導(dǎo)致如下情況,某些幀顯示的畫面內(nèi)容就會(huì)與上一幀的畫面相同

3.渲染時(shí)會(huì)出現(xiàn)的問題
GPU過度繪制
GPU的繪制過程,就跟刷墻一樣,一層層的進(jìn)行,16ms刷一次.這樣就會(huì)造成,圖層覆蓋的現(xiàn)象,即無用的圖層還被繪制在底層,造成不必要的浪費(fèi).
過度繪制查看工具
在手機(jī)端的開發(fā)者選項(xiàng)里,有OverDraw監(jiān)測工具,調(diào)試GPU過度繪制工具,
其中顏色代表渲染的圖層情況,分別代表1層,2層,3層,4層覆蓋.
我的魅族手機(jī)的Monitor GPU Rendering
計(jì)算渲染的耗時(shí)
任何時(shí)候View中的繪制內(nèi)容發(fā)生變化時(shí),都會(huì)重新執(zhí)行創(chuàng)建DisplayList,渲染DisplayList,更新到屏幕上等一 系列操作。這個(gè)流程的表現(xiàn)性能取決于你的View的復(fù)雜程度,View的狀態(tài)變化以及渲染管道的執(zhí)行性能。
舉個(gè)例子,當(dāng)View的大小發(fā)生改變,DisplayList就會(huì)重新創(chuàng)建,然后再渲染,而當(dāng)View發(fā)生位移,則DisplayList不會(huì)重新創(chuàng)建,而是執(zhí)行重新渲染的操作.
當(dāng)你的View過于復(fù)雜,操作又過于復(fù)雜,就會(huì)計(jì)算渲染時(shí)間超過16ms,產(chǎn)生卡頓問題.

渲染耗時(shí)呈現(xiàn)工具
工具中,不同手機(jī)呈現(xiàn)方式可能會(huì)有差別.分別關(guān)于StatusBar,NavBar,激活的程序Activity區(qū)域的GPU Rending信息。激活的程序Activity區(qū)域的GPU Rending信息。
界面上會(huì)滾動(dòng)顯示垂直的柱狀圖來表示每幀畫面所需要渲染的時(shí)間,柱狀圖越高表示花費(fèi)的渲染時(shí)間越長。
中間有一根綠色的橫線,代表16ms,我們需要確保每一幀花費(fèi)的總時(shí)間都低于這條橫線,這樣才能夠避免出現(xiàn)卡頓的問題。
每一條柱狀線都包含三部分,
藍(lán)色代表測量繪制Display List的時(shí)間,
紅色代表OpenGL渲染Display List所需要的時(shí)間,
黃色代表CPU等待GPU處理的時(shí)間。
4.如何優(yōu)化
有人會(huì)說這些小地方,不值得優(yōu)化.但是當(dāng)你用的是低配機(jī)器,內(nèi)存到飽和,CPU運(yùn)算到達(dá)飽和,就像一個(gè)界面要做很多交互,繪制,加載圖片,請(qǐng)求網(wǎng)絡(luò).后,一個(gè)小問題就會(huì)導(dǎo)致頁面卡頓(就像我手機(jī)的淘寶客戶端...),OOM,項(xiàng)目崩潰.
是的,這就是力量~
Android系統(tǒng)已經(jīng)對(duì)它優(yōu)化
在Android里面那些由主題所提供的資源,例如Bitmaps,Drawables都是一起打包到統(tǒng)一的Texture紋理當(dāng)中,然后再傳遞到 GPU里面,這意味著每次你需要使用這些資源的時(shí)候,都是直接從紋理里面進(jìn)行獲取渲染的。
我們要做的優(yōu)化
扁平化處理,防止過度繪制OverDraw
**1.每一個(gè)layout的最外層父容器 是否需要? **
**2.布局層級(jí)優(yōu)化 **
進(jìn)行檢測時(shí),可能會(huì)讓多種檢測工具沖突,用Android Device Monitor的時(shí)候,最好關(guān)閉相關(guān)手機(jī)上的開發(fā)者檢測工具開關(guān).
查看自己的布局,深的層級(jí),是否可以做優(yōu)化.
渲染比較耗時(shí)(顏色就能看出來),想辦法能否減少層級(jí)以及優(yōu)化每一個(gè)View的渲染時(shí)間.
Hierarchy Viewer工具
他是查看耗時(shí)情況,和布局樹的深度的工具.
3.圖片選擇
Android的界面能用png最好是用png了,因?yàn)?strong>32位的png顏色過渡平滑且支持透明。jpg是像素化壓縮過的圖片,質(zhì)量已經(jīng)下降了,再拿來做9path的按鈕和平鋪拉伸的控件必然慘不忍睹,要盡量避免。
對(duì)于顏色繁雜的,照片墻紙之類的圖片(應(yīng)用的啟動(dòng)畫面喜歡搞這種),那用jpg是最好不過了,這種圖片壓縮前壓縮后肉眼分辨幾乎不計(jì),如果保存成png體積將是jpg的幾倍甚至幾十倍,嚴(yán)重浪費(fèi)體積。
4.清理不必要的背景
5.當(dāng)背景無法避免,盡量用Color.TRANSPARENT
因?yàn)橥该魃?code>Color.TRANSPARENT是不會(huì)被渲染的,他是透明的.
//優(yōu)化前
//優(yōu)化前: 當(dāng)圖片不為空,ImageView加載圖片,然后統(tǒng)一設(shè)置背景
Bean bean=list.get(i);
if (bean.img == 0) {
Picasso.with(getContext()).load(bean.img).into(holder.imageView);
}
chat_author_avatar.setBackgroundColor(bean.backPic);
//優(yōu)化后
//優(yōu)化后:當(dāng)圖片不為空,ImageView加載圖片,并設(shè)置背景為TRANSPARENT;
//當(dāng)圖片為空,ImageView加載TRANSPARENT,然后設(shè)置背景為無照片背景
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);
}
-------------對(duì)比結(jié)果--------------------
這里寫圖片描述
6.優(yōu)化自定義View的計(jì)算
View中的方法OnMeasure,OnLayout,OnDraw.在我們自定義View起到了決定作用,我們要學(xué)會(huì)研究其中的優(yōu)化方法.
學(xué)會(huì)裁剪掉View的覆蓋部分,增加cpu的計(jì)算量,來優(yōu)化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.總結(jié)
性能優(yōu)化其實(shí)不僅僅是一種技術(shù),而是一種思想,你只聽過它的高大上,卻不知道它其實(shí)就是各個(gè)細(xì)節(jié)處的深入研究和處理.
當(dāng)然,有的時(shí)候也需要自己進(jìn)行權(quán)衡效果和性能,根據(jù)需求進(jìn)行選擇.
還有,Android Device Monitor 是個(gè)好東西~簡直就是性能優(yōu)化大本營,性能優(yōu)化的工具基本都在其中.
所以在平時(shí)的開發(fā)過程中,養(yǎng)成良好的思考習(xí)慣,是第一步~
寫代碼的時(shí)候要想:
1.你的代碼是不是多余?
2.你的對(duì)象有沒有必要在循環(huán)中創(chuàng)建?
3.你的計(jì)算方法是不是最優(yōu)?
畫界面的時(shí)候要想:
1.布局是否有背景?
2.是否可以刪掉多余的布局?
3.自定義View是否進(jìn)行了裁剪處理?
4.布局是否扁平化,移除非必需的UI組?
最后,Android Device Monitor 是個(gè)好東西~ 性能優(yōu)化的工具基本都在其中.
關(guān)于我:
我的github: https://github.com/ccj659/
我的簡書: 簡書地址傳送門
我的CSDN博客: http://blog.csdn.net/ccj659/article
我的上一篇性能優(yōu)化文章: 深入android內(nèi)存泄漏
QQ進(jìn)階交流群:570381965