標籤:
本文地址:http://blog.csdn.net/iamws/article/details/51636175
第二篇:理論
通過之前前篇介紹的工具,我們知道了應該怎麼樣去擷取要分析的資料,但是也僅僅局限在於怎麼樣擷取資料,而沒有深入資料分析,這一篇主要講解的是UI重新整理這塊部分android理論知識,有了這些知識後,對於上面的資料該怎麼分析,你就胸有成竹了。
ps:本文只是個人理解後的總結,並不會深入源碼層次分析,如有錯誤,還請麻煩各位幫忙指正~
這篇文章要解決的理論問題如下:
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
ps:android GC相關的知識推薦可以看看老羅寫的部落格,友情連結:http://blog.csdn.net/luoshengyang/article/details/41822747
可以看到上面除了主動代碼或者訊號調用以外,其它的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
黃油計劃:http://blog.csdn.net/innost/article/details/8272867
視頻搜尋: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都是根據項目中效能問題真實改編而來,這塊預計需要花比較多的業餘時間了,可能會分多個不同側重點推出,敬請期待哈~
ps:這篇文章花了一個晚上去整理思緒後寫的,本來留了很多補充的地方,不過要開始下一階段工作了,而且這一系列的文章就是需要結合資料去實際分析最佳化效能的,重點在於執行個體上,所以就先推出這個版本吧,以後再考慮加上補充拉~
author:[email protected]
轉載必須註明出處:本文地址:http://blog.csdn.net/iamws/article/details/51636175
android效能最佳化實戰理論篇