Android應用性能優化之優化列表頭像過度繪制[一]


  操作的是否順暢、卡頓,決定着整體的流暢程度。
 
  事實上android跟iphone的差別,個人覺得很大程度上決定於流暢程度,無論是動畫,還是列表滑動等相關操作,流暢與否,對於用戶而言,雖然他們說不出來哪里不對,但是卡與不卡的反饋卻是很直接的。

  Google也設法想改變這局面。在4.0之后的應用(最低版本4.0)默認開啟硬件加速,並且4.1新加了一個Project Butter(黃油計划),設法將渲染幀速提升到60fps。
 
  雖然能看到android的進步,但在實際中,並沒有特別的變化,原因很簡單。一方面android在3.0之后才開始支持GPU渲染。另外一方面,也是想做此系列的重要原因,應用的順暢不能僅靠系統去努力,開發者在代碼中給予力量才是最重要的。
 
  坐看市面上應用,拋開需求,僅在操作流程程度上,國外應用至少甩開國內應用N條街,從最基本的硬件加速開啟,到整體控件優化方案,這就是為什么很多4.1以上的手機,在使用應用上依然找不到那種屬於順暢的感受。
 
   先申明,菜鳥一枚,很多東西都只是初步猜想,能力有限,短時間內無法深入確認原理,並且有些東西是反編其他應用看來的,可能達到了效果,但一些關鍵的類似適配之類的代碼沒看到。所以有錯在所難免的嘛,希望體諒。
 
Android應用性能優化之優化列表頭像過度繪制
 
  下圖為自己的應用蜂巢的主題時間軸列表:
  因為在開啟了硬件加速后,還是沒有達到60幀數的要求,存在卡頓的感覺。所以我們一步步來改善他吧     
 
  這次我們先解決一個很普遍的問題,那就是過度繪制。
  而且過度繪制,其實很好理解,舉個例子,一個白色背景的窗口,上面有一個黑色背景的布局,最后上面顯示的是一個按鈕。事實上最后顯示的時候,按鈕下面的白色背景和黑色背景是看不到的,但在繪制的時候確都畫上去了。
 
  當然過度繪制是無法完全避免的,適度的是可以接受的,但過多了之后就會造成性能影響。引用大牛的分析就是:
設備的內存帶寬是有限的,當過度繪制導致應用需要更多的帶寬(超過了可用帶寬)的時候性能就會降低。帶寬的限制每個設備都可能是不一樣的。一個好的參考目標就是控制過度繪制為2X;這說明您可以繪制一次屏幕,然后在上面繪制最多2次內容,一共繪制每個像素3次。
 
  android提供了對應的工具進行檢測,在4.2的開發者選項中,提供了一個叫做“顯示GPU過度繪制”的選項。當然,所要分析的頁面必須要開啟硬件加速。
 
  通過顏色來區別標示過度繪制的情況:藍色1x過度繪制、綠色2x過度繪制、淡紅色3x過度繪制和紅色超過4x過度繪制。
 
  我們先用這個來看下:
  頭像跟顯示的主體圖片都是淡紅色,並且區域占的很大,所以我們先依次把這兩個圖片過度繪制的問題解決了。
 
  知道了要優化的地方,我們就要分析具體問題了,其實優化過度繪制的通用思路比較簡單,就是要知道具體每一層都繪制了什么,刪去完全沒必要的層級。當然這是最基本的思路,要實現這個思路我們就需要用到另外一個工具Tracer for OpenGL。
 
  這個工具能夠記錄每一次繪畫的動作,並且能展現出來,這樣我們就能看到具體的繪畫情況了。
 
   使用要求:
  工具需要 連接到一個運行Android 4.1(API級別16)或更高設備上的運行,所以記得開啟4.1及以上模擬器或真機來進行分析調試
 
  使用方法:
  打開ADT的Tracer for OpenGL Es標簽(默認好像是沒添加的,需要手動添加下),有一個向上的箭頭。點擊后會彈出選項對話框,如下。
 
 
   Device:選擇需要調試的設備。
   Application Package:填入包名,也就是AndroidMainfest.xml里面謝的那個,比如com.example.XXXX
   Activity to launch:填入需要測試的頁面路徑。也就是除了包名外的身下部分。比如test.xxxActivity
   Data Collection Options:有三個選項,第一個選項是快速定位對應幀,具體的效果就是能看到每一幀最終生成的樣子,第二個選項是關鍵,查看每幀的每個繪制命令及效果。
   Destination File:存放數據的路徑,建議找個剩余空間大的地方。
 
  都選擇完后,點擊Trace后就開始分析。設備會直接進入所填寫的分析界面,如果沒有進入,或者進入后工具的狀態沒有任何改變,就說明失敗了,需要重新設置並重來。當設備上顯示完整你需要分析的界面部分時,稍過一會,就可以點擊stop來查看結果了。
[個人經驗:第三方工具就沒有一個不奇葩的,此工具也不例外。依照之前經驗,建議點擊之前,對應的設備記得把鎖屏界面解開,否者失敗的可能性很大]
 
  在結果中可以看到每個發送給gpu的GL命令,選擇每一幀,能在Frame Summary中看到每一幀的最終效果。並且能在Detail界面中看到每一個繪畫命令執行后的界面狀態。
 
 以下為頭像區域的繪制情況:
 (1) (2)
  能看出,其實是先將頭像的圖片畫了出來,然后再將一個4個圓角的圖片畫在之前圖片的上面。形成圓角頭像的效果。
 
  以上講的只是一個整體的思路,通過對應的工具,查找出每一步的繪畫步驟,來規避多余的繪制命令,從而達到減少過度繪制的目的。
 
  
  接下來開始解決這個頭像的問題,按常理將要實現這個效果,就必須要圖片覆蓋,所以用通用的思路是無法解決這個過度繪制的問題。當初這個制作圓角頭像的方法是從path那里學來的,那我們就看看,path是不是也無法解決此問題。
 
  好吧,藍色,證明path能解決這個過度繪制。
 
  看下反編出來的代碼,應該能猜個大概出來。
 
  首先先看看布局,是不是普通的ImageView.
  path首頁里的布局東西真心多,終於在茫茫大海中找到了我們的目標。
  果真,重寫了,不是普通的控件,CacheableProfilePhoto。Google了一下,看來不是開源的空間,那就直接從代碼里猜吧。
 
 
  此控件繼承於OverlayImageView,那我們先看看OverlayImageView是啥
 
  恩恩,看來就是他了,作為一個繼承於ImageView的家伙,果斷直接看OnDraw方法。 [常人是無法忍受path這滿地都是食物名稱為變量的反編代碼.坑爹啊]
  發現繪制的整體流程是①先獲取一個DrawingCanvas類型的變量,如果此變量為空,則初始化它。②然后似乎有一個方法,將此變量作為參數傳入了進去。③之后此變量可以返回一個bitmap,④最后將這個bitmap畫在canvas上。
 
  流程挺簡單,簡直的關鍵就在於,畫上去的bitmap到底是個啥東西。
 
  要弄懂這個東西,我們就必須弄懂 DrawingCanvas是什么。
  看來是一個Canvas,並且擁有一個類型為Bitmap的變量,有一個方法來返回這個bitmap變量。
  在DrawingCanvas的代碼里,似乎看不到繪制的處理,說明另有其他地方,之前在OverlayImageView看到個方法將此類型的變量作為參數,那我們就看看那個方法做了什么事情。
 
  在 OverlayImageView中有個叫做protected void wheatbiscuit(DrawingCanvas paramDrawingCanvas)的方法
 
代碼片段一
 
 
代碼片段二
  這下有點清晰了,此方法中對傳入的DrawingCanvas變量繪制了兩次。那現在我們方便來看下這兩次方便都畫了什么。
 
  這是第一次,那很明顯,應該是屬於這個ImageView的圖片資源
Drawable localDrawable1 = getDrawable();

  這是第二次,redwine是在setOverlayResource方法中被賦值的,此方法傳入的是i = localTypedArray.getResourceId(0, -1);最后定為到的是app:overlay="@drawable/moment_avatar" ,那就明白了,第二次繪制上去的,其實就是覆蓋在上面的畫有4個圓角的圖片。

Bitmap localBitmap2 = ((BitmapDrawable)this.redwine).getBitmap();

 

  終於真相大白了,總結下,其實path 就是將原本每次都要繪制兩次的圖片,事先就繪制好並且緩存起來,這樣就不會過度繪制了
  來,我們看看效果:
  看吧,頭像變藍了吧?!成功。
  當然,還有很多代碼細節這里就不詳細展開說了,畢竟同樣的思想,可以有不同的代碼實現邏輯。
  最主要還是想傳達一個思路。
 
  或許這思路,大家都懂,就我剛知道吧。o(╯□╰)o
 
  這次關於頭像的優化就先寫到這吧,廢話多了點,請見諒。
  不過奇怪的是,正文圖片的過度繪制,從分析來看似乎正常了耶=。=,忘記之前改了哪了...假動作晃到自己了。明天認真翻代碼下。
 
  下篇會繼續正文圖片的相關優化寫下去...

 


免責聲明!

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



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