Android性能分析

查看MAT中的Bitmap

使用ImageMagick 更方便 convert -size 'width'x'height' -depth 8 filename.rgba filename.png

MAC安裝ImageMagick

每種顏色代表每一幀渲染過程中需要完成的某一件事情,因為6.0之前的三種顏色不大能夠清晰地幫助我們定位性能問題的具體原因,所以從6.0開始,將每一幀的渲染過程拆分成了8個步驟,每個步驟一種顏色,每種顏色的意義如下:


image.png
  1. Swap Buffers:表示處理任務的時間,也可以說是CPU等待GPU完成任務的時間,線條越高,表示GPU做的事情越多;
  2. Command Issue:表示執行任務的時間,這部分主要是Android進行2D渲染顯示列表的時間,為了將內容繪制到屏幕上,Android需要使用Open GL ES的API接口來繪制顯示列表,紅色線條越高表示需要繪制的視圖更多;
  3. Sync & Upload:表示的是準備當前界面上有待繪制的圖片所耗費的時間,為了減少該段區域的執行時間,我們可以減少屏幕上的圖片數量或者是縮小圖片的大小;
  4. Draw:表示測量和繪制視圖列表所需要的時間,藍色線條越高表示每一幀需要更新很多視圖,或者View的onDraw方法中做了耗時操作;
  5. Measure/Layout:表示布局的onMeasure與onLayout所花費的時間,一旦時間過長,就需要仔細檢查自己的布局是不是存在嚴重的性能問題;
  6. Animation:表示計算執行動畫所需要花費的時間,包含的動畫有ObjectAnimator,ViewPropertyAnimator,Transition等等。一旦這里的執行時間過長,就需要檢查是不是使用了非官方的動畫工具或者是檢查動畫執行的過程中是不是觸發了讀寫操作等等;
  7. Input Handling:表示系統處理輸入事件所耗費的時間,粗略等于對事件處理方法所執行的時間。一旦執行時間過長,意味著在處理用戶的輸入事件的地方執行了復雜的操作;
  8. Misc Time/Vsync Delay:表示在主線程執行了太多的任務,導致UI渲染跟不上vSync的信號而出現掉幀的情況;

盡管現在已經有比較先進的圖片加載組件類似Glide,Facebook Freso, 或者老牌Universal-Image-Loader,但是有時就是需要手動拿到一個bitmap或者drawable,特別是在一些可能會頻繁調用的場景(比如ListView的getView),怎樣盡可能對bitmap進行復用呢?這里首先需要明確的是對同樣的圖片,要 盡可能復用,我們可以簡單自己用WeakReference做一個bitmap緩存池,也可以用類似圖片加載庫寫一個通用的bitmap緩存池,可以參考 GlideBitmapPool[8]的實現。
我們也來看看系統是怎么做的,對于類似在xml里面直接通過android:background或者android:src設置的背景圖片,以ImageView為例,最終會調用Resource.java里的loadDrawable:

{
   // Next, check preloaded drawables. These may contain unresolved theme
   // attributes.
   final ConstantState cs;
   if (isColorDrawable)
   {
       cs = sPreloadedColorDrawables.get(key);
   }else{
       cs = sPreloadedDrawables[mConfiguration.getLayoutDirection()].get(key);
   }

   Drawable dr;
   if (cs != null) {
       dr = cs.newDrawable(this);
   } else if (isColorDrawable) {
       dr = new ColorDrawable(value.data);
   } else {
       dr = loadDrawableForCookie(value, id, null);
   }

   ...
   
   return dr;
}

可以看到實際上系統也是有一份全局的緩存,sPreloadedDrawables, 對于不同的drawable,如果圖片時一樣的,那么最終只會有一份bitmap(享元模式),存放于BitmapState中,獲取drawable時,系統會從緩存中取出這個bitmap然后構造drawable。而通過BitmapFactory.decodeResource()則每次都會重新解碼返回bitmap。所以其實我們可以通過context.getResources().getDrawable再從drawable里獲取bitmap,從而復用bitmap,然而這里也有一些坑,比如我們獲取到的這份bitmap,假如我們執行了recycle之類的操作,但是假如在其他地方再使用它是那么就會有”Canvas: trying to use a recycled bitmap android.graphics.Bitmap”異常。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容