Android性能优化——布局优化
一、绘制原理
- 对于Android手机来说,画面渲染依赖两个硬件一个是CPU 一个是GPU
- CPU负责计算显示内容,比如视图创建,布局计算,图片解码,文本绘制
- GPU 负责栅格化,UI元素绘制到屏幕上,将一些组件,比如Button bitmap 拆分成不同的像素进行显示,然后完成绘制,这个操作相对来说非常耗时,所以引入GPU 来完成栅格话操作
- 具体到Android系统当中,文字的显示,首先通过CPU 换算成纹理,然后在传给GPU 来渲染的,而图片的显示,首先通过CPU 的计算,然后加载到内存当中,然后在传给GPU 渲染
二、Android的16毫秒原则
每隔16毫秒就会发出VSync 信号触发UI渲染,这就意味着Android系统要求每一帧在16ms 这个时间之内来完成
大多数Android设备屏幕刷新频率在60Hz
三、工具
systrace
- 关注Frames
- 正常绿色圆点,丢帧黄色或者红色
- 发现丢帧后可查看Alert 栏
Layout Inspector
- Android studio自带的工具
- 查看视图层次结构
- 用法:Tools 下的 Layout Inspector 选择进程,就能生成当前界面的检测信息
Choreographer
- 获取Fps 线上使用,具有实时性
- api 16之后
Choreographer.getInstance).postFrameCallback
四、优雅的获取界面布局耗时
1.常规方式
背景:页面进来界面会卡顿,可能是布局引起的,获取每个界面的加载耗时
实现:复写方法,手动埋点,在setContentView 处手动埋点,上传到服务端
问题:不够优雅,代码有侵入性
2.AOP 实现
切面点:Activity 的setContentView
@Around(“execution(*
android.app.Activity.setContentView())”)
call 是切在哪个位置,方法前面还是后面
execution 是在方法的里面
3.ARTHook 实现
- 获取每一个控件的加载耗时
- 低侵入性
- LayoutInflater.Factory
Factory2能实现对父View 的控制,创建View 的一个Hook 可以在创建View的过程做一些事情
使用场景:定制生成控件的过程,可以将全部layout 当中的某一个控件改造成自定义控件,比如说可以将项目中所有的TextView 改造成具有特殊能力的TextView
五、统计每个控件加载耗时
分析:createView 方法里面来真正View 的创建,在这里会做一个hook ,匹配一个具体的控件,如TextView ImageView
如果name 为TextView 会调用createTextView 实际创建出来AppCompatTextView
由于Android系统版本的升高,高版本的控件有一些高级的效果,这些高级效果怎么样来兼容低版本呢?就是通过AppCompat 这些控件以及LayoutInflate.Factory 来实现的低版本的这些控件会被替换成AppCompat 控件,这些控件都是在support 包当中的,因此高版本的控件在低版本中的兼容
LayoutInflate.Factory 要在setContentView 之前创建
异步Inflate
背景介绍
- 布局文件读取慢:IO过程
- 创建View慢:反射(比new慢3倍)
不能设置Layoutinflater.Factory (自定义解决)
注意View中不能有依赖主线程的操作
六、布局加载优化实战
- 布局加载慢的原因是由于IO操作、反射
- AsyncLayoutinflater只是缓解
方案一:Java代码写布局
- 本质上解決了性能问题
- 引入新问题:不便于开发、可维护性差
方案二:×2C介绍
- 保留XML优点,解决其性能问题
- 开发人员使用XML,加载Java代码
- 原理:APT 编译期翻译XML为Java代码
- 使用:
AnnotationProcessor com.zhangyue.we1.1.2’
• implementation ‘com.hangyue.we:×2c-lib:1.0.6’
• @XmI(layouts = “activity_main”)
- 问题:
部分属性Java不支持
失去了系统的兼容 ( AppCompat )
七、视图绘制优化实战
- 测量:确定大小
- 布局:确定位置
- 绘制:绘制视图
1.绘制的特点,性能瓶颈
- 每个阶段耗时
- 自顶而下的遍历
- 会触发多次
2.布局绘制优化
- 减少View树层级
- 宽而浅,避免窄而深
使用ConstraintLayout
- 实现几乎完全扁平化布局
- 构建复杂布局性能更高
- 具有Relativelayout和LinearLayout特性
- 不嵌套使用Relativelayout
- 不在嵌套LinearLayout中使用weight
- 使用merge标签 :减少一个层级,只能用于根View
3.过度绘制
- 一个像素最好只被绘制一次
- 调试GPU过度绘制
- 蓝色可接受,蓝色代表只绘制一次
八、避免过度绘制方法
- 去掉多余背景色,减少复杂shape使用
- 避免层级叠加
- 自定义View使用clipRect屏蔽被遮盖View绘制
- Viewstub:高效占位符、延迟初始化
- onDraw中避免:创建大对象、耗时操作
- TextView优化
九、模拟问题
1. 你在做布局优化过程中用到了哪些工具?
在做布局优化的过程中用到了很多工具,这些工具都有不同的使用场景,根据场景的不同使用的工具也是不一样的,比如:
- 我要统计线上的FPS,就用到Choreographer 他可以拿到整体的帧率,还可以带到线上,这个方法拿到的帧率几乎是实时的,满足我们的需要。
- 要去优化布局加载所带来的时间消耗,那就需要每个布局的耗时,就使用了AOP 的方式,是一种Hook,无侵入性,同时也不需要其他的开发同事介入就可以方便的获取到每个界面的加载耗时。
- 在线下开发环节,使用的是Systrace,Layout Inspector 。Systrace 可以很轻松的看到每一帧的具体耗时,以及这一帧在布局中真正做了什么,而Layout Inspector 很方便的看到每个界面的布局层级,帮助我们对这个层级进行优化
2. 布局为什么会导致卡顿,你是怎么优化的
为什么会导致卡顿?
- layout inflate 对布局进行加载的时候,首先将xml 文件映射到内存当中,这个是一个IO的过程,这个IO 的过程可能会导致卡顿,布局加载的时候是一个反射的过程,反射的过程也可能导致卡顿,如果布局的层级比较深,遍历的次数也就变多,也就导致资源的消耗
- 针对于加载layout的优化,可以使用异步inflate 的方式,它的核心原理是在子线程对layout 进行加载然后回调给主线程,直接使用的view ,所以主线程不会阻塞,主线程没有时间消耗,时间消耗在子线程中,这种方案是一种缓解的思路
- 使用X2C 方案,在开发的过程中还是使用的xml 布局,在编译期间会将xml 布局自动转化为Java类,用Java类去写布局他没有io的慢,它是直接new的对象,没有反射的慢,这样从根本上解决问题
如何优化?
- 减少布局的层级,使用ConstraintLayout 减少布局的嵌套
- 建立起卡顿的监控信息 采用AOP
3. 做完布局优化有哪些成果产出?
- 首先建立起了体系化的监控手段,线下加线上,针对线下,采用AOP 和ARTHook 很方便的获取到每个布局他加载的耗时,以及每个控件的加载耗时
- 同时可以方便的看到布局层级是多少,针对于线上用户,收集了FPS ,就可以知道用户在哪些界面有丢帧的情况
还没有评论,来说两句吧...