Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> android性能優化實戰理論篇

android性能優化實戰理論篇

編輯:關於Android編程

第二篇:理論

通過之前前篇介紹的工具,我們知道了應該怎麼樣去獲取要分析的數據,但是也僅僅局限在於怎麼樣獲取數據,而沒有深入數據分析,這一篇主要講解的是UI刷新這塊部分android理論知識,有了這些知識後,對於上面的數據該怎麼分析,你就胸有成竹了。

 

這篇文章要解決的理論問題如下:

1、什麼是內存洩漏,內存溢出,這塊對性能有什麼影響以及android GC的種類

2、網上說的android UI刷新是60fps/16ms一次是什麼樣的一回事

3、android的整體繪制原理

4、view動畫和屬性動畫區別

5、過度繪制會導致什麼問題

6、硬件加速制的時候你是否對畫布使用了lockcanvas()方法

7、哪些操作是耗費CPU的

8、其他性能優化注意點(想到後再加上)

 

如果對以上知識已經了解的話,那就麻煩跳過這篇,期待下一篇實戰舉例吧(┬_┬)

 

好了,開始

第一:什麼是內存洩漏,內存溢出,這塊對性能有什麼影響以及android GC的種類?

(這塊需要對GC有一些了解,了解了java GC的這塊知識後下面就很好理解了)

由於android是個移動設備上的操作系統,沒有了PC上的硬件支持,所以對於系統cpu,內存資源都需要額外關注,這才是為什麼要很注意性能問題;

內存洩漏:簡單講就是之前創建了對象,也分配了內存給它,但是後來這個對象沒用了,本來應該要交給系統去回收,但是對象卻由於一直被GC ROOT上存在引用鏈導致無法回收,然後這部分內存就一直被占用了,如果每次操作都會生成這個對象的話,將會逐漸吞噬app的內存剩余空間,最終結果就是導致內存被吃完了,然後拋出溢出異常

 

內存溢出:常稱OOM,out of memory,一般玩圖片加載不注意的時候很容易碰到這個異常,這個其實是一個app在任何android手機運行的時候,都會是 linux給fork出虛擬機(例如:dalvik)進程跑的,而每個虛擬機創建的時候就會給他當前系統規定的可使用內存大小,如果你的app某個操作導致要使用的內存超過了這個分配值,而且GC後剩余的內存都沒辦法給你足夠的空間去分配,那麼這個異常就出來了

 

android GC種類:我們在android的logcat中輸入GC關鍵字,可以看到GC基本上有以下這幾類:

GC_FOR_MALLOC: 表示准備在堆上分配內存時內存不夠觸發的GC

GC_CONCURRENT: 表示是在當前內存達到一定設置量後導致觸發的GC

GC_BEFORE_OOM: 表示是在准備拋OOM異常之前進行GC

GC_EXPLICIT: 表示是程序中主動調用System.gc、VMRuntime.gc接口或者收到信號時觸發的主動GC
 

可以看到上面除了主動代碼或者信號調用以外,其它的GC都是在內存分配過程中調用的,而GC過程是需要涉及到CPU計算的,同時GC過程分為掛起或者與其他線程並行兩種模式,這時候大家就明白為什麼GC的時候會突然出現性能下降或者應用界面容易無響應了吧,這也證明了內存洩漏的危害,因為內存洩漏後就會導致可使用內存的降低,隨著對象對內存的新申請,很容易就會觸發頻繁GC了,所以解決內存洩漏是解決性能優化的一個重要方法。

 

第二:網上說的android UI刷新是60fps/16ms一次是什麼樣的一回事?

我們可以看到基本上網上都有說android的UI是60fps的,也就換算成1000/60=16ms刷新一次,這種說法是怎麼來的,而且這個是誰定義的呢?

答案當然是google,為什麼google這樣定義,這是因為做過android 2.x系列開發的人都知道,安卓當時的流暢度是完敗給IOS的,流暢性被吐槽的特別多,google當時也發現這個問題很嚴重,於是在2012年的google i/o大會上,正式推出了4.1版本,同時也推出了黃油計劃,這個黃油計劃目標就是提升整體的流暢度,這裡面就涉及到所謂的單緩存、雙重緩存和三重緩存通道的機制區別,而且為了確保每次刷新的幀數相同,制定了vsync信號,這個信號就是通知系統的SurfaceFlinger要去執行渲染整個頁面了

那麼現在簡單介紹一下這塊緩存流程和部分技術:

什麼是SurfaceFlinger:官方解釋是:Every window that is created on the Android platform is backed by a surface. All of the visible surfaces rendered are composited onto the display by SurfaceFlinger.

就是說每個window背後都是surface,而所有的可見surface最後組成並要顯示出來的時候,靠的就是SurfaceFlinger去渲染的,而SurfaceFlinger是一個system級的服務,依賴的是binder通信機制,由client去請求建立surface並最終完成渲染通信過程

所以android的渲染邏輯是:vsync信號是由系統每隔16ms發出一次,發出後就是通知SurfaceFlinger需要渲染了,渲染的數據哪裡來呢?就是上面說的(單緩存、雙重緩存和三重緩存技術通道裡面存儲的)存儲的緩存隊列,而緩存隊列裡面的數據怎麼來的,就是CPU和GPU計算得出的頁面繪制數據;

\

\

 

  簡單說下這幾個緩存機制吧,單緩存:CPU/GPU繪制後的數據與屏幕顯示讀取的數據在同一個緩存隊列中;但是由於計算頻率和顯示頻率一般是不同的,這種緩存模式最容易導致的就是畫面撕裂,也就是說看到的界面容易出現上一個幀和當前幀在同一個畫面組成一張圖,而這並不是我們想要的流暢度;

 

雙重緩存:給了2個buffer隊列,一個給CPU/GPU繪制後存放數據,一個給屏幕渲染的時候去讀取數據,同時制定2個buffer之間的數據交換的規則,最終解決上面畫面撕裂的問題;不過CPU和GPU共用同一個buffer,而我們知道,CPU和GPU如果在休眠,只有收到vsync信號才會開始處理下一個的數據,如果某次當vsync信號發出時,而且剛好又是一個耗時操作,CPU或者GPU還在處理上一個幀的計算,就會出現CPU或者GPU正占有著buffer,那麼那個沒有占有buffer的CPU/GPU也沒辦法處理了,所以就只好繼續再休眠一個幀了,所以三重緩存就出來了

 

三重緩存:比上面的雙重緩存還要多一個緩存,目地就是說硬件你們就各在個的緩存裡面做事情,這樣最大化利用各個硬件的計算頻率優勢,同時也規范制定好了之間數據的交換規則,這樣,android的UI流暢度一下就上來了

 

順便借用一下google io 2012的宣傳資料裡面的圖,直觀展示上面三者的區別和影響吧:

\

圖2-1:單緩存下界面展示撕裂的原因,因為CPU/GPU頻率高,Display頻率低,當界面開始讀取下一個數據時候,就會把途中0.015的圖讀出來,就撕裂啦

 

 

\

圖2-1 單緩存實現圖

 

 

\

圖2-3雙緩存實現圖

 

 

\

圖2-4 三緩存實現圖

 

 

所以由上面的分析我們得知,如果想要你的應用順暢展示界面,一需要確保你的每次渲染提交的計算過程能夠在16ms內完成,二要減少沒必要的高幀率計算,讓你的渲染幀率保持跟系統刷新的頻率,這樣就能很大程度上釋放cpu,同時保證流暢度,也就是性能優化的一個方法了。

此處可以參考文章:

官方渲染:http://source.android.com/devices/graphics/index.html

視頻搜索:google發表的Android 21個性能優化典范教學

 

 

第三個:android的整體繪制原理

這塊就純粹是看android的view繪制源碼了,網上應該也挺多的解釋的,具體就是系統層的windowmanager怎麼跟ViewRoot關聯起來,然後再怎麼從頂級DecorView到內部的view做measure,layout,draw等等,這裡主要提的一點就是子布局的測量過程是需要依賴父布局的測量結果和子布局的屬性一起才能決定最終子view測量結果的

為什麼要單獨列出來說這個呢,主要是考慮如果你的子布局一直有變化,將會帶來整體所有上層父布局全都重走measure過程,而measure是CPU消耗的,下面是我demo的例子,testlinearlayout為父布局容器,嵌套兩層,裡面再動態插入子布局容器testtextview,布局樹和日志如下:

\

\

看了圖後你就明白,為什麼一直說UI嵌套不能過深了,這塊主要解決的就是UI布局嵌套過深,如果子布局變化過快帶來的CPU計算損耗,是一個重要的性能優化點。

 

可以參考書籍:《深入理解android 卷II》《android開發藝術探索》裡面有從framework層和java層兩塊詳細分開介紹整個流程,這裡就不多說了(主要太懶了(*^__^*) ,相關資料太多了)

 

 

第四個:view動畫和屬性動畫的區別

這塊想必大家一般都知道這些動畫在應用層上最大的區別,view動畫是假移動,view實際上還在原來位置,點擊什麼的事件也是在原位置監聽,而屬性動畫是真移動,通過改變view的translation屬性值,這樣在新位置也能觸發事件

不過這裡要說的是他們的性能區別,先說總結的吧,優先使用屬性動畫去做,屬性動畫對性能優化提升最大

why?

//TODO 插入動畫對比圖1、2、3

過幾天補上,做好的圖不見了- - !!!

 

 

 

第五個:過度繪制會導致什麼問題

什麼叫做過度繪制?我們知道屏幕渲染最終依賴的都是手機硬件上的每一個小像素點的顏色的改變去實現的,而且由上面所知,目前android采用的是16ms的刷新機制,也就是說你看到的每一幀圖像之間有16ms的間隔期,但是在這個間隔期內,你的計算可能對某些像素點指定了要做多次繪制,當系統最終渲染的時候等於也會對這個像素點按你的要求單幀內也繪制多次,導致有些時候實際上是做了很多無用功,例如指定了某個viewgroup背景顏色是白色,然後裡面放入一個子view,但是你讓這個子view背景色還是白色後,當系統開始渲染這一幀就會按你的意思,某個像素繪制一個白色,然後又繪制一次白色,這樣是不是感覺後面的繪制實際上是多余而且沒有必要的,所以說過度繪制的削減對於UI流暢度的提升非常重要。

所以過度繪制導致的問題是CPU/GPU做了很多無用功,界面渲染過程也浪費了比較多的繪制資源

解決也很簡單,1、減少背景設置重疊,2、減少不必要的view重疊,3、減少復雜布局實現(要和產品撕逼的一點,嘿嘿)等

下面隨機找了2款線上APP的渲染展示圖,都是登陸功能的,大家可以稍微看看:(只做技術知識研究分享展示用,對應APP的開發GG別打我)


\

\

 

 

第六個:想用硬件加速的時候你是否對畫布使用了lockcanvas()方法

目前主流手機基本都有硬件加速功能了,而且硬件加速在渲染上對我們的CPU計算幫助很大,所以大家都想去用好它,我們很多時候在用surfaceview, surfacetextture去做一些復雜自定義view繪制的時候也特別希望用上它去幫忙加速,但是事與願違,原來官方文檔中寫了一句話,導致我們以為是用了硬件加速幫忙,結果反而不是的,這句話就是官方文檔中著重注意的:

\

翻譯過來大概就是說如果獲取畫布采用lockcanvas()方法的時候,那麼這個畫布的繪制就不會是硬件加速了

這個優化點首先是要感謝工作中的同事,是他在優化過程中發現並提出的,同時證明我看文檔的時候還是不太仔細,好像這句話我當時都忽略了,慚愧~

所以這塊大家希望能讓硬件加速幫上忙的話特別注意一下下,也算作一個優化注意點

 

 

第七個:哪些操作是耗費CPU的

這塊只能說以往經常碰到的點提一下吧

1、布局計算

2、動畫播放

3、圖片加載

4、本地IO

5、反射機制

6、多重內部循環

7、線程

8、鎖

9、某些api裡面有的繪制計算

10、view的各種變形操作

11、等等等(想到再補)

是不是覺得跟java的很像啊,沒錯,基本就是一樣的,java耗費的地方,android也一樣,這塊就不著重說了,大家注意實現某些邏輯的時候多考慮下是否有更好的實現方案或者更佳的api調用就ok了

 

第八個:其它分析點

想到再補充,不過都是集中在前篇說的這幾個問題裡面:內存洩漏,布局層次過深,measure計算過多,動畫繪制問題,io,自定義控件的draw裡面對象創建,線程濫用,handler裡面丟了個外部對象不清除,內部類與匿名類問題等;

 

 

下一篇將會開始結合實例數據代碼分析,不過這裡面要寫很多對比demo,畢竟公司項目的代碼不能拿出來分析的,但是demo都是根據項目中性能問題真實改編而來,這塊預計需要花比較多的業余時間了,可能會分多個不同側重點推出,敬請期待哈~

 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved