布局优化措施大纲:
-
过度绘制
Overdraw(过度绘制):描述的是屏幕上的某个像素在同一帧的时间内被绘制了多次。在多层次的UI结构里面,如果不可见的UI也在做绘制的操作,就会导致某些像素区域被绘制了多次,浪费大量的CPU以及GPU资源,因为底下被覆盖区域怎么重绘也没有意义。当view内容发生改变时,整个view都会重绘,如果层次太“丰富”,那性能就会严重下降。
过渡绘制层次:
无色: 意味着没有overdraw。像素只画了一次。
蓝色: 意味着overdraw 1倍。像素绘制了两次。大片的蓝色还是可以接受的(若整个窗口是蓝色的,可以摆脱一层)。
绿色: 意味着overdraw 2倍。像素绘制了三次。中等大小的绿色区域是可以接受的但你应该尝试优化、减少它们。
浅红: 意味着overdraw 3倍。像素绘制了四次,小范围可以接受。
暗红: 意味着overdraw 4倍。像素绘制了五次或者更多。这是错误的,要修复它们。
界面绘制层次颜色图:
-
合理选择容器
LinearLayout易用,效率高,表达能力有限。RelativeLayout复杂,表达能力强,效率稍逊,但是relativeLayout可以减少布局层级,用一层写出复杂的嵌套。ConstraintLayout可以更好地减少层级,平铺布局层级。
Viewstub 高效占位符
我们经常会遇到这样的情况,运行时动态根据条件来决定显示哪个View或布局。常用的做法是把View都写在上面,先把它们的可见性都设为View.GONE,然后在代码中动态的更改它的可见性。这样的做法的优点是逻辑简单而且控制起来比较灵活。但是它的缺点就是,耗费资源。虽然把View的初始可见View.GONE但是在Inflate布局的时候View仍然会被Inflate,也就是说仍然会创建对象,会被实例化,会被设置属性。也就是说,会耗费内存等资源。
ViewStub是一个轻量级的View,它一个看不见的,默认不占布局位置,在Inflate布局的时候,只有ViewStub会被初始化。在要显示的时候调用 ((ViewStub)findViewById(R.id.stub_view)).setVisibility(View.VISIBLE);让其可见,ViewStub内部的布局就会被Inflate和实例化。
viewstub常用来引入那些默认不会显示,只在特殊情况下显示的布局,如进度布局、网络失败显示的刷新布局、信息出错出现的提示布局等。
Merge
当布局根布局没有实际属性仅仅起的是一个简单的父viewgroup时,可以用merge来代替,merge不会作为一个层级进行绘制。3434s
-
业务逻辑简化
在设计业务逻辑时,尽量不要过度设计。华丽的App界面毕竟会牺牲绘制性能。在功能完成的前提下,尽量设计层级简单操作简单。
-
优化工具
Lint工具检测布局层次
lint检测工具开启2项布局性能的检测指标,如果有布局嵌套层次深的,会检测报出问题。
点击Code菜单的Inspect Code触发Lint检测。
Layout Inspector
在Tools -> Layout Inspector,选择对应相应进程,打开界面层级检查
可根据view类型和对应id选择查看,可切换2D、3D功能进行查看。
布局加载优化
xmlPullParser IO操作,布局越复杂,IO耗时越长。
createView 反射,View越多,反射调用次数越多,耗时越长,但是这必须达到一定量级才会有明显影响。
直接解决就是不使用IO和反射技术,我们这里介绍侧面缓解,即将布局加载和解析放在子线程中,等到inflate操作完成后再将结果回调到主线程中,即使用Android为我们提供的AsyncLayoutInflater类来进行异步布局加载。
AsyncLayoutInflater
AsyncLayoutInflater是google提供的方案,让LayoutInflater.inflater过程通过子线程来做:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
new AsyncLayoutInflater(this).inflate(R.layout.activity_main,null, new AsyncLayoutInflater.OnInflateFinishedListener(){
@Override
public void onInflateFinished(View view, int resid, ViewGroup parent) {
setContentView(view);
rv = findViewById(R.id.tv);
rv.setLayoutManager(new V7LinearLayoutManager(MainActivity.this));
rv.setAdapter(new RightRvAdapter(MainActivity.this));
}
});
}
布局绘制监控
获取布局文件加载耗时的方法
val start = System.currentTimeMillis()
setContentView(R.layout.activity_layout_optimize)
val inflateTime = System.currentTimeMillis() - start
这种方法很简单,因为setContentView是同步方法,如果想要计算耗时,直接将前后时间计算相减即可得到结果了
优化布局层级及复杂度
1.使用ConstraintLayout,可以实现完全扁平化的布局,减少层级
2.RelativeLayout本身尽量不要嵌套使用
3.嵌套的LinearLayout中,尽量不要使用weight,因为weight会重新测量两次
4.推荐使用merge标签,可以减少一个层级
5.使用ViewStub延迟加载
避免过度绘制
1.去掉多余背景色,减少复杂shape的使用
2.避免层级叠加
3.自定义View使用clipRect屏蔽被遮盖View绘制
参考:
https://www.jianshu.com/p/9e8f88eac490
https://mp.weixin.qq.com/s?__biz=MzA5MzI3NjE2MA==&mid=2650271556&idx=1&sn=f9c444f67322deb2a818ae2fca3f0e52#rd