Android性能优化-过度绘制解决方案

转载 2017年11月29日 00:00:00

过度绘制:

屏幕上某一像素点在一帧中被重复绘制多次,就是过度绘制。 
下图中多个卡片跌在一起,但是只有第一个卡片是完全可见的。背后的卡片只有部分可见。但是android系统在绘制时会将下层的卡片进行绘制,接着再将上层的卡片进行绘制。但其实,下层卡片不可见的部分是不需要进行绘制的,只有可见部分才需要进行绘制。 
640?wx_fmt=jpeg&wxfrom=5&wx_lazy=1

依据过度绘制的层度可以分成: 
- 无过度绘制(一个像素只被绘制了一次) 
- 过度绘制x1(一个像素被绘制了两次) 
- 过度绘制x2(一个像素被绘制了三次) 
- 过度绘制x3(一个像素被绘制了四次) 
- 过度绘制x4+(一个像素被绘制了五次以上)

查看自己应用的过度绘制情况:

方法一:通过开发者选项开启GPU过度绘制调试 
Android手机的开发者选项中有『调试 GPU 过度绘制』的选项: 
0?wx_fmt=jpeg

点开后后选择『显示过度绘制区域』: 
0?wx_fmt=png

方法二:通过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里面设置的。 
0?wx_fmt=jpeg

我们只要在自己的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里的裁切类似。

典型的例子,抽屉布局,找了网易云音乐开刀: 
0?wx_fmt=gif 
注意观察左侧抽屉打开的时候,抽屉布局和背后布局重叠在一起了,此时整个屏幕一多半都变成了红色,过度绘制严重。

在抽屉布局弹出时,抽屉布局是不透明的,也就是说抽屉布局背后挡住的内容布局是不需要绘制的,而网易云进行了绘制,导致抽屉布局所在区域的像素点绘制了多次。

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实现: 
0?wx_fmt=gif 
应用中虽然内容区域是红色,但是抽屉视图拉出来之后,抽屉视图的过度绘制情况却比内容区域未被挡住的部分少。


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进行裁切。

  • 尽量减少视图的深度,来减少视图树的遍历过程。

转载请注明出处:http://blog.csdn.net/a740169405/article/details/53896497

0?wx_fmt=jpeg


性能优化过度绘制

调试GPU来查看过度绘制过度绘制对性能会产生不必要的开销,如果能减少不必要的绘制,将有助于性能的提升。以下以在手机上进行 Debug GPU overdraw 举例:在开发者选项里,打开【调试GPU过...
  • u013003052
  • u013003052
  • 2016年01月28日 20:41
  • 671

Android性能优化之GPU过度绘制与图形渲染优化

Android之GPU过度绘制与图形渲染优化写在前面的话本文主要对过度绘制和图形渲染做一个概念性的描述,和简单的优化措施。 如果你已对过度绘制有过一些了解,那么你应该明白,仅是简单的层级优化对过度绘...
  • u010255127
  • u010255127
  • 2015年10月16日 21:20
  • 5025

Android过度绘制优化心得

随着产品的更新上线,迎来了短暂的休闲期,借此来总结一下最近完成的任务–有关Android过度绘制的优化。过度绘制概念:     在屏幕一个像素上绘制多次(超过一次)。比如一个TextView后有背景,...
  • moyameizan
  • moyameizan
  • 2015年08月20日 14:24
  • 6120

Android过度绘制的优化

转自:http://jaeger.itscoder.com/android/2016/09/29/android-performance-overdraw.html?hmsr=toutiao.io&u...
  • Hknock
  • Hknock
  • 2017年07月04日 16:28
  • 408

# Android UI性能优化—过度绘制篇

Android UI性能优化——过度绘制篇过度绘制(overdraw)过度绘制介绍每过几年,就会有传闻说某个博物馆在用x光扫描一副无价的名画之后,发现画作的作者其实重用了老的画布,在名画的底下还藏着另...
  • qq_19711823
  • qq_19711823
  • 2017年03月24日 10:33
  • 559

Android过度绘制深度优化---View提前绘制

原理介绍:上一篇文章对过度绘制和View优化做了大概的简介和介绍。我们知道,引起过度绘制的根本原因是 背景(背景包裹背景图片、颜色、形状、边框等等)。那么通常你在网络查找过度绘制优化策略,不外乎减少...
  • u010255127
  • u010255127
  • 2015年11月07日 15:47
  • 4851

Android性能优化-过度绘制解决方案

过渡绘制:屏幕上某一像素点在一帧中被重复绘制多次,就是过渡绘制。 下图中多个卡片跌在一起,但是只有第一个卡片是完全可见的。背后的卡片只有部分可见。但是android系统在绘制时会将下...
  • a740169405
  • a740169405
  • 2017年01月08日 14:15
  • 4679

Android UI性能优化 -- GPU过度绘制

首先我参考了鸿洋大神的文章,真心写的很棒,受益匪浅:点击打开链接 当然过度绘制是无法完全避免的,适度的是可以接受的,但过多了之后就会造成性能影响。引用大牛的分析就是: 设备的内存带宽是有限的,当...
  • qq_17766199
  • qq_17766199
  • 2015年11月06日 10:38
  • 1602

Android性能优化-过度绘制解决方案

转载请注明出处:http://blog.csdn.net/a740169405/article/details/538964971 过度绘制:屏幕上某一像素点在一帧中被重复绘制多次,就是过度绘制。 ...
  • qiyei2009
  • qiyei2009
  • 2017年01月11日 21:59
  • 179

Android开发者选项之GPU过度绘制

做设计的人很少能知道GPU过度绘制是个什么鬼?跟设计有什么关系?今天就让大家了解了解,首先来普及一下Android开发者选项中的Debug GPU overdraw。 GPU过度绘制定义 如果你粉...
  • u012209506
  • u012209506
  • 2016年10月11日 14:51
  • 2768
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Android性能优化-过度绘制解决方案
举报原因:
原因补充:

(最多只允许输入30个字)