【轉】Android效能最佳化-過度繪製解決方案

來源:互聯網
上載者:User

標籤:使用   sdn   例子   像素   開發人員   alt   不可   string   其他   

轉載請註明出處:http://blog.csdn.net/a740169405/article/details/53896497過度繪製:

螢幕上某一像素點在一幀中被重複繪製多次,就是過度繪製。 
中多個卡片跌在一起,但是只有第一個卡片是完全可見的。背後的卡片只有部分可見。但是android系統在繪製時會將下層的卡片進行繪製,接著再將上層的卡片進行繪製。但其實,下層卡片不可見的部分是不需要進行繪製的,只有可見部分才需要進行繪製。 

依據過度繪製的層度可以分成: 
- 無過度繪製(一個像素只被繪製了一次) 
- 過度繪製x1(一個像素被繪製了兩次) 
- 過度繪製x2(一個像素被繪製了三次) 
- 過度繪製x3(一個像素被繪製了四次) 
- 過度繪製x4+(一個像素被繪製了五次以上)

查看自己應用的過度繪製情況:

方法一:通過開發人員選項開啟GPU過度繪製調試 
Android手機的開發人員選項中有『調試 GPU 過度繪製』的選項: 

點開後後選擇『顯示過度繪製地區』: 

方法二:通過adb命令開啟GPU過度繪製調試 
當然,如果每次都進入系統設定嫌麻煩,可以使用adb命令進行開啟和關閉: 
開啟『調試 GPU 過度繪製』:

adb shell setprop debug.hwui.overdraw show
  • 1

關閉『調試 GPU 過度繪製』:

adb shell setprop debug.hwui.overdraw false
  • 1

執行命令之後可能需要重新啟動你當前開發的應用。

顏色與過度繪製:
  • 原色:沒有過度繪製
  • 藍色:1 次過度繪製
  • 綠色:2 次過度繪製
  • 粉色:3 次過度繪製
  • 紅色:4 次及以上過度繪製

在平時的開發中,如果出現粉色及以上的過度繪製情況。說明過度繪製以及很嚴重了。需要進行最佳化。

最佳化過度繪製:

1. 去除Activity內建的預設背景顏色: 
查看Android源碼裡的Theme主題,如下:

<style name="Theme">    ...    <!-- Window attributes -->    <item name="windowBackground">@drawable/screen_background_selector_dark</item>    ...</style>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

也就是說繼承Theme這個style的風格,預設情況下,建立一個Activity都是有背景的。正常情況下,很多介面其實是不需要背景的。

下面是華為內建天氣APP的首頁,我們可以看到文字部分以及表徵圖部分都是綠色,說面已經是第三層過度繪製了,其中背後天氣圖是一層,文字又是一層,正常來說應該只有兩層,也就是文字和表徵圖應該是藍色。那麼這多出來的一層應該就是Activity內建的背景色了。也就是theme裡面設定的。 

我們只要在自己的AppTheme裡面去除該背景色即可:

<style name="AppTheme" parent="android:Theme.Light.NoTitleBar">    <item name="android:windowBackground">@null</item></style>
  • 1
  • 2
  • 3

或者在Activity的onCreate方法中:

getWindow().setBackgroundDrawable(null);
  • 1

2.使用Canvas的clipRect和clipPath方法限制View的繪製地區 
一個Activity對應有一個Canvas,也就是畫布,畫布的概念就是一個畫板,這個畫布提供了很多的API,我們可以通過調用畫布的API來繪圖以及對畫布做一些操作,clipRect方法用來裁切畫布上的一個矩形地區,該矩形地區用Rect對象來描述。調用了clipRect之後,畫布的可繪製地區減小到和Rect指定的矩形地區一樣大小。所有的繪製將限制在該矩形範圍之內。這裡的裁切概念和PS裡的裁切類似。

典型的例子,抽屜布局,找了網易雲音樂開刀: 
 
注意觀察左側抽屜開啟的時候,抽屜布局和背後布局重疊在一起了,此時整個螢幕一多半都變成了紅色,過度繪製嚴重。

在抽屜布局彈出時,抽屜布局是不透明的,也就是說抽屜布局背後擋住的內容布局是不需要繪製的,而網易雲進行了繪製,導致抽屜布局所在地區的像素點繪製了多次。

google官方在android.support.v4.widget包下有DrawerLayout.java類。使用來實現抽屜布局的。該類在重寫了drawChild方法:

@Overrideprotected boolean drawChild(Canvas canvas, View child, long drawingTime) {    final int height = getHeight();    // 判斷是否是內容視圖    final boolean drawingContent = isContentView(child);    int clipLeft = 0, clipRight = getWidth();    // 記錄當前畫布資訊    final int restoreCount = canvas.save();    if (drawingContent) {        // 只有在繪製內容視圖時才進行裁切        final int childCount = getChildCount();        for (int i = 0; i < childCount; i++) {            final View v = getChildAt(i);            if (v == child || v.getVisibility() != VISIBLE ||                    !hasOpaqueBackground(v) || !isDrawerView(v) ||                    v.getHeight() < height) {                // 如果child是內容視圖/視圖不可見/視圖背景透明/不是抽屜視圖/child高度小於父布局高度                // 則不做畫布裁切                continue;            }            if (checkDrawerViewAbsoluteGravity(v, Gravity.LEFT)) {                // 盒子在左側時裁切的left和right                final int vright = v.getRight();                if (vright > clipLeft) clipLeft = vright;            } else {                // 盒子在右側時裁切的的left和right                final int vleft = v.getLeft();                if (vleft < clipRight) clipRight = vleft;            }        }        // 裁切畫布        canvas.clipRect(clipLeft, 0, clipRight, getHeight());    }    // 繪製子視圖    final boolean result = super.drawChild(canvas, child, drawingTime);    // 回複到裁切之前的畫布    canvas.restoreToCount(restoreCount);}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

drawChild方法在ViewGroup類的dispatchDraw方法內被調用,用來繪製子視圖,DrawerLayout類通過重寫該方法,因為在所有孩子視圖繪製之前都會調用drawChild方法,但是這裡只需要對內容地區視圖做裁切,當繪製內容地區視圖時,取得抽屜視圖的位置資訊,如果抽屜視圖可見、背景為不透明、抽屜高度和父布局高度一致時,取得抽屜視圖左、上、右、下邊緣在canvas中的位置資訊。接著進行裁切,將內容視圖未被擋住的部分地區裁切出來,並把裁切完的canvas交由子View進行繪製,這樣,內容地區只有在裁切後的地區才會繪製,其他地區不進行繪製。待子View繪製完之後,恢複Canvas到裁切之前的狀態,因為一個Window下的所有View都使用的是同一個Canvas,所以需要恢複狀態給其他子View使用。

下面看一個系統裡的“下載”APP,使用的是DrawerLayout實現: 
 
應用中雖然內容地區是紅色,但是抽屜視圖拉出來之後,抽屜視圖的過度繪製情況卻比內容地區未被擋住的部分少。

3. ImageView的background和imageDrawable重疊 
Android中,所有的view均可以設定background。ImageView除了能夠設定background之外,還能設定ImageDrawable。

在開發中,很多時候需要顯示圖片,在圖片載入出來之前通常是需要顯示一張預設圖片的,很多時候會使用ImageView的background屬性來設定預設背景圖,而imageDrawable來設定需要載入的圖片。這樣會導致一個問題,當圖片載入到頁面後,預設背景圖被擋住了,但是卻仍然然需要繪製,導致過度繪製情況的發生。

解決方案是把背景圖和真正載入的圖片都通過imageDrawable方法進行設定。

總結
  • Android中一個window對應一個Canvas,window下的所有視圖(View/ViewGroup)使用的都是同一個canvas,視圖樹的父節點在調用子視圖的View.draw之前,會對Canvas進行裁切,裁切的地區就是View在螢幕中所佔的矩形地區,這也就是為什麼超過View邊界的內容會被裁切掉的原因。
  • 既然過度繪製值一個像素點被繪製多次,我們只要保證圖片或者背景顏色不要疊加在一起即可。正確的方式應該是盡量減少帶背景的View產生重疊地區。如果重疊,使用canvas的clipRect進行裁切。
  • 盡量減少視圖的深度,來減少視圖樹的遍曆過程。

【轉】Android效能最佳化-過度繪製解決方案

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.