什么是Overdraw?
Overdraw就是过度绘制,是指在一帧的时间内(16.67ms)像素被绘制了多次,理论上一个像素每次只绘制一次是最优的,但是由于重叠的布局导致一些像素会被多次绘制,而每次绘制都会对应到CPU的一组绘图命令和GPU的一些操作,当这个操作耗时超过16.67ms时,就会出现掉帧现象,也就是我们所说的卡顿,所以对重叠不可见元素的重复绘制会产生额外的开销,需要尽量减少Overdraw的发生。
Android提供了测量Overdraw的选项,在开发者选项-调试GPU过度绘制(Show GPU Overdraw),打开选项就可以看到当前页面Overdraw的状态,就可以观察屏幕的绘制状态。该工具会使用三种不同的颜色绘制屏幕,来指示overdraw发生在哪里以及程度如何,其中:
没有颜色: 意味着没有overdraw。像素只画了一次。
蓝色: 意味着overdraw 1倍。像素绘制了两次。大片的蓝色还是可以接受的(若整个窗口是蓝色的,可以摆脱一层)。
绿色: 意味着overdraw 2倍。像素绘制了三次。中等大小的绿色区域是可以接受的但你应该尝试优化、减少它们。
浅红: 意味着overdraw 3倍。像素绘制了四次,小范围可以接受。
暗红: 意味着overdraw 4倍。像素绘制了五次或者更多。这是错误的,要修复它们。

下面结合案例进行讲解优化:



下面我们打开开发者选项的Debug CPU OverDraw
主界面存在问题

第一、在按钮overdraw上面就有个红色的过度绘制区域
第二、在文本框This is test的布局中也是红色过度绘制区域
首先, 要解决上面的文问题我们要分析activity_main.xml的布局文件,发现这里使用了多个嵌套的LinearLayout布局,而且每个LinearLayout都会使用一次android:background设置一次自己的背景颜色,他们造成了过度绘制。仔细分析在其中一个嵌套ImageView的LinearLayout布局背景颜色与最外层的背景颜色是一样的,属于不需要的背景色,因此将这个LinearLayout中的android:background属性删除,这时发现文本框布局已经不再是红色了。
下图是优化后的截图:

再者,去掉window的默认背景;当我们使用了Android自带的一些主题时,我们的activity往往会被设置一个默认的背景,这个背景是被DecorView持有的。其实整个ui其实还含有一个隐含的绘制效果,那边是在activity中,使用setContentView(R.layout.activity_main)设置布局的时候,android会自动填充一个默认的背景,而在这个UI中,我们使用了填充整个app的背景,因此不需要默认背景。
去掉window的背景可以在onCreate中调用
getWindow().setBackgroundDrawable(null);
优化后:

OVERDARWVIEW界面存在问题
我们看下图,可以看出这个自定义图案引起了过度绘制。


首先,这个也是填充了整个ui界面的绘制图片,因此我们也在activity中的onCreate方法中添加getWindow().setBackgroundDrawable(null);取消默认绘制。
再者,发现过度绘制问题是由于OverDrawView类中的ondraw方法中多次绘制了矩形导致的。通过分析得知,颜色为GRAY的矩形的高度其实不需要设置为整个屏幕的高度,它的高度只需要设置为它所显示范围的高度就可以了,因此可以设为height/4。其他的矩形也是同样的道理,因此更改这里的代码为:
优化后:

BUSYONDREW界面存在问题
当我们点击BUSYONDRAW按钮的时候,我们发现明显的卡顿现象。在开发者选项中打开Profile GPU rendering选项,然后在次点击BUSYONDRAW按钮,发现这个页面绘制渲染时间非常非常高!可能是我的模拟器的问题哦。。。。好像其他界面也非常的高。。。。。在初始的时候,蓝色绘制时间占满整个屏幕高度,这是造成卡顿的重要原因。
卡顿现象是由ondraw方法中的for循环中的打印字符串引起的

未解决这个卡顿问题,我们将耗时的操作放在一个子线程里:



最后是一个内存泄漏的问题:ondraw中也不宜创建Paint()对象,因为app会频繁调用ondraw对象,会造成内存泄漏,因此需要将其提取为全局变量。

网友评论