安卓ondraw刷新视图_实战 Android中的UI过度绘制

本文探讨了Android应用中UI过度绘制导致的卡顿问题,并提供了解决方案。通过分析过度绘制的原因,如不必要的背景颜色和重复的绘制操作,提出通过调整布局和精简代码来优化。具体措施包括移除冗余背景、禁用默认背景、优化自定义View的onDraw方法以及避免在onDraw中执行耗时操作。实践案例展示了优化前后性能的提升。
摘要由CSDN通过智能技术生成

相信很多人都有这种经历,在使用app的过程中,突然间发现程序虽然在运行,但是这里停顿一下,那里停顿一下的卡顿现象,就像看上网看视频一样,缓冲不过来,视频很卡,不能连续的看下去。造成这样原因有很多,其中一种就是UI被过度绘制了。

UI过度绘制简单的来说是指在一个界面中有很多元素,但是我们只需要更新某一小块的元素,app却把所有的元素都刷新一遍,这就造成过度绘制。

overdraw_hidden_view.png

过度绘制造成UI卡顿的原因是因为它浪费大量的CPU以及GPU资源。手机原本为了保持视觉的流畅度,其屏幕刷新频率是60hz,即在1000/60=16.67ms内更新一帧。如果没有完成任务,就会发生掉帧的现象,也就是我们所说的卡顿。

这其中的原理比较复杂,大家可以看看大神是怎么说的,这里给个胡凯大神的文章地址:

debug GPU overdraw

有问题就必然有解决办法,在Android系统内部也有一个神器可以查看app的UI的过度绘制情况,在开发者选项中有个debug GPU overdraw(调试GPU过度绘制),打开之后有off(关闭),show overdraw areas(显示过度绘制区域),show areas for Deuteranomaly(为红绿症患者显示过度绘制区域)

overdraw.png

我们选择show overdraw areas,发现整个手机界面的颜色变了,在打开过度绘制选项后,其中的蓝色,淡绿,淡红,深红代表了4种不同程度的Overdraw情况,我们的目标就是尽量减少红色Overdraw,看到更多的蓝色区域。

color.png

Profile GPU rendering

其次android系统还内置了Profile GPU rendering工具,这个工具也是在开发者选项中打开,它能够以柱状图的方式显示当前界面的渲染时间

Profile GPU rendering.png

蓝色代表测量绘制的时间,或者说它代表需要多长时间去创建和更新你的DisplayList.在Android中,一个视图在可以实际的进行渲染之前,它必须被转换成GPU所熟悉的格式,简单来说就是几条绘图命令,复杂点的可能是你的自定义的View嵌入了自定义的Path. 一旦完成,结果会作为一个DisplayList对象被系统送入缓存,蓝色就是记录了需要花费多长时间在屏幕上更新视图(说白了就是执行每一个View的onDraw方法,创建或者更新每一个View的Display List对象).

橙色部分表示的是处理时间,或者说是CPU告诉GPU渲染一帧的地方,这是一个阻塞调用,因为CPU会一直等待GPU发出接到命令的回复,如果柱状图很高,那就意味着你给GPU太多的工作,太多的负责视图需要OpenGL命令去绘制和处理.

红色代表执行的时间,这部分是Android进行2D渲染 Display List的时间,为了绘制到屏幕上,Android需要使用OpenGl ES的API接口来绘制Display List.这些API有效地将数据发送到GPU,最总在屏幕上显示出来.

在这里也放一个大神关于Profile GPU rendering的介绍

下面我们开始对UI多度绘制开始实战吧!

实战项目地址

初始界面的问题

刚打开这个项目,我们就发现了在第一个有过度绘制问题,效果如下

main界面过度绘制.PNG

存在问题

在按钮overdraw上面就有个红色的过度绘制区域

在文本框This is test的布局中也是红色过度绘制区域

解决方法

要解决这个问题,我们首先需要分析这是怎么引起的。分析到activity_main.xml的布局文件时,发现这里使用了多个嵌套的LinearLayout布局,而且每个LinearLayout都会使用一次android:background设置一次自己的背景颜色,他们造成了过度绘制。

仔细分析在其中一个嵌套ImageView的LinearLayout布局背景颜色与最外层的背景颜色是一样的,属于不需要的背景色,因此将这个LinearLayout中的android:background属性删除,这时发现文本框布局已经不再是红色了

第一次优化.png

咋看之下一切都很完美,但其实整个ui其实还含有一个隐含的绘制效果,那边是在activity中,使用setContentView(R.layout.activity_main)设置布局的时候,android会自动填充一个默认的背景,而在这个UI中,我们使用了填充整个app的背景,因此不需要默认背景,取消也很简单,只需要在activity中的onCreate方法中添加这么一句就行了

getWindow().setBackgroundDrawable(null);

现在看最终优化效果

最后结果.png

OVERDRAWVIEW页面的问题

在overdrawviewactivity中只有一个自定义的图案,而这个自定义的图案引起了过度绘制的问题

OVERDRAWVIEW过度绘制.PNG

解决方法

首先这个也是填充了整个ui界面的绘制图片,因此我们也在activity中的onCreate方法中添加getWindow().setBackgroundDrawable(null);取消默认绘制。

继续研究,发现过度绘制问题是由于OverDrawView类中的ondraw方法中多次绘制了矩形导致的,代码如下:

@Override

protected void onDraw(Canvas canvas) {

super.onDraw(canvas);

int width = getWidth();

int height = getHeight();

mPaint.setColor(Color.GRAY);

canvas.drawRect(0, 0, width, height, mPaint);

mPaint.setColor(Color.CYAN);

canvas.drawRect(0, height/4, width, height, mPaint);

mPaint.setColor(Color.DKGRAY);

canvas.drawRect(0, height/3, width, height, mPaint);

mPaint.setColor(Color.LTGRAY);

canvas.drawRect(0, height/2, width, height, mPaint);

}

通过分析得知,颜色为GRAY的矩形的高度其实不需要设置为整个屏幕的高度,它的高度只需要设置为它所显示范围的高度就可以了,因此可以设为height/4。

其他的矩形也是同样的道理,因此更改这里的代码为:

@Override

protected void onDraw(Canvas canvas) {

super.onDraw(canvas);

int width = getWidth();

int height = getHeight();

mPaint.setColor(Color.GRAY);

canvas.drawRect(0, 0, width, height/4, mPaint);

mPaint.setColor(Color.CYAN);

canvas.drawRect(0, height/4, width, height/3, mPaint);

mPaint.setColor(Color.DKGRAY);

canvas.drawRect(0, height/3, width, height/2, mPaint);

mPaint.setColor(Color.LTGRAY);

canvas.drawRect(0, height/2, width, height, mPaint);

}

优化的界面

最终优化.png

BUSYONDRAW频繁绘制

当我们点击BUSYONDRAW按钮的时候,我们发现明显的卡顿现象。在开发者选项中打开Profile GPU rendering选项,然后在次点击BUSYONDRAW按钮,发现这个页面绘制渲染时间已经突破天际了!

绘制时间1.png

在初始的时候,蓝色绘制时间占满整个屏幕高度,这是造成卡顿的重要原因。

解决方法

首先卡顿现象是由ondraw方法中的for循环中的打印字符串引起的

for (int i = 0; i < 1000; i++) {

System.out.println("canvas = [" + canvas + "]" + i);

}

我们将其提取出来放在另一个线程中运行。

先是创建一个线程池:

private ExecutorService pool = Executors.newCachedThreadPool();

将要运行的耗时操作封装成Runable对象并通过一个方法获取:

@NonNull

private Runnable getCommand(final Canvas canvas) {

return new Runnable() {

@Override

public void run() {

for (int i = 0; i &lt; 1000; i++) {

System.out.println("canvas = [" + canvas + "]" + i);

}

}

};

}

最后在onDraw里运行放在子线程里运行:

pool.execute(getCommand(canvas));

现在进入也不会卡顿了。

其次在ondraw中也不宜创建Paint()对象,因为app会频繁调用ondraw对象,会造成内存泄漏,因此需要将其提取为全局变量。

最后绘制了多个圆形图案也造成了一定程度的卡顿,但由于功力不够,暂时无法优化。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值