Android性能优化-UI优化

一.Android绘制原理

View绘制过程

  1. CPU:执行应用层的measure、layout、draw等操作,绘制完成后将数据提交给GPU
  2. GPU:进一步处理数据,并将数据缓存起来
  3. 屏幕:由一个个像素点组成,以固定的频率(16.6ms,即1秒60帧)从缓冲区中取出数据来填充像素点
    总结一句话就是:CPU 绘制后提交数据、GPU 进一步处理和缓存数据、最后屏幕从缓冲区中读取数据并显示。
    在这里插入图片描述

双缓冲机制

看完上面的流程图,我们很容易想到一个问题,屏幕是以16.6ms的固定频率进行刷新的,但是我们应用层触发绘制的时机是完全随机的 。
如果在GPU向缓冲区写入数据的同时,屏幕也在向缓冲区读取数据,怎么办?所以,在屏幕刷新中,Android系统引入了双缓冲机制, GPU只向Back Buffer中写入绘制数据,且GPU会定期交换Back Buffer和Frame Buffer,交换的频率也是60次/秒,这就与屏幕的刷新频率保持了同步。
在这里插入图片描述
虽然我们引入了双缓冲机制,但是我们知道,当布局比较复杂,或设备性能较差的时候,CPU并不能保证在16.6ms内就完成绘制数据的计算,所以这里系统又做了一个处理。当你的应用正在往Back Buffer中填充数据时,系统会将Back Buffer锁定。如果到了GPU交换两个Buffer的时间点,你的应用还在往Back Buffer中填充数据,GPU会发现Back Buffer被锁定了,它会放弃这次交换。这样做的后果就是手机屏幕仍然显示原先的图像,这就是我们常常说的掉帧

布局加载原理

由上面可知,导致掉帧的原因是CPU无法在16.6ms内完成绘制数据的计算。而之所以布局加载可能会导致掉帧,正是因为它在主线程上进行了耗时操作,可能导致CPU无法按时完成数据计算,布局加载主要通过setContentView来实现,一起来看看它的时序图
在这里插入图片描述

布局加载优化

1. AsyncLayoutInflater方案

syncLayoutInflater 是来帮助做异步加载 layout 的, 默认情况下 setContentView 函数是在 UI 线程执行的,其中有一系列的耗时动作:Xml的解析、View的反射创建等过程同样是在UI线程执行的,AsyncLayoutInflater 就是来帮我们把这些过程以异步的方式执行,保持UI线程的高响应。

	  @Override
	    protected void onCreate(@Nullable Bundle savedInstanceState) {
	        super.onCreate(savedInstanceState);
	        new AsyncLayoutInflater(AsyncLayoutActivity.this)
	                .inflate(R.layout.async_layout, null, new AsyncLayoutInflater.OnInflateFinishedListener() {
	                    @Override
	                    public void onInflateFinished(View view, int resid, ViewGroup parent) {
	                        setContentView(view);
	                    }
	                });
	        // 别的操作
	    }

这样做的优点在于将UI加载过程迁移到了子线程,保证了UI线程的高响应 缺点在于牺牲了易用性,同时如果在初始化过程中调用了UI可能会导致崩溃

2. X2C方案

X2C是掌阅开源的一套布局加载框架。
它的主要是思路是在编译期,将layout翻译生成对应的java文件,这样对于开发人员来说写布局还是写原来的xml,但对于程序来说,运行时加载的是对应的java文件。
使用时如下所示,使用X2C.setContentView替代原始的setContentView即可

// this.setContentView(R.layout.activity_main);
X2C.setContentView(this, R.layout.activity_main);

X2C优点

  • 在保留xml的同时,又解决了它带来的性能问题
  • 据X2C统计,加载耗时可以缩小到原来的1/3

X2C问题

  • 部分属性不能通过代码设置,Java不兼容
  • 将加载时间转移到了编译期,增加了编译期耗时

3. Compose方案

Compose 是 Jetpack 中的一个新成员,Compose使用纯kotlin开发,使用简洁方便,Compose是未来android UI开发的方向

二.布局优化

布局优化的本质就是减少View的层级。常见的布局优化方案如下:

  • 使用ConstraintLayout,可以实现完全扁平化的布局,减少层级

  • 嵌套的LinearLayout中,尽量不要使用weight,因为weight会重新测量两次

  • 在LinearLayout和RelativeLayout都可以完成布局的情况下优先选择RelativeLayout,可以减少View的层级,但是注意相同组件可能RelativeLayout绘制时间长

  • 使用 < include > 标签将常用的布局组件共同的部分抽取出来,以便复用。

  • 通过 < ViewStub > 标签来加载不常用的布局,延迟加载(需要的时候在activity中加载出来)

  • 使用 < Merge > 标签来减少布局的嵌套层次

三.绘制优化

绘制优化是指View的onDraw方法要避免执行大量的操作,这主要体现在两个方面:

1. 去掉多余背景色,减少复杂shape的使用

2. 自定义View使用clipRect屏蔽被遮盖View绘制

3.onDraw 中不要创建新的局部对象。

因为onDraw方法可能会被频繁调用,这样就会在一瞬间产生大量的临时对象,这不仅占用了过多的内存而且还会导致系统更加频繁gc,降低了程序的执行效率。

4.onDraw方法中不要做耗时的任务,

不能执行成千上万次的循环操作,尽管每次循环都很轻量级,但是大量的循环仍然十分抢占CPU的时间片,这会造成View的绘制过程不流畅。

按照Google官方给出的性能优化典范中的标准,View的绘制频率保证60fps是最佳的,这就要求每帧绘制时间不超过16ms(16ms = 1000/60),虽然程序很难保证16ms这个时间,但是尽量降低onDraw方法中的复杂度总是切实有效的。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

幸福在路上wellbeing

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值