一、相关知识点
VSYNC信号、Overdraw(过度绘制)
二、UI优化工具
1、Hierarchy Viewer
性能优化工具知识梳理(4) – Hierarchy Viewer
2、真机调试-调试GPU过度绘制
性能优化工具知识梳理(3) – 调试GPU过度绘制 & GPU呈现模式分析
比如三星手机的开发者选项中有调试GPU过度渲染工具。
颜色:蓝色<浅绿<浅红<深红
分别代表:绘制一次<绘制两次<绘制三次<绘制四次及以上
3、Lint检查
三、UI优化技巧
1、减少布局层级
相信很多人为了图布局方便各种布局嵌套,结果导致层级过深。提升布局性能的关键点是尽量保持布局层级的扁平化,避免出现重复的嵌套布局。
这里介绍几种方法避免,有些用起来很简单有些确实会比较繁琐。
1)利用View的特殊属性
a、利用TextView滑动属性来避免嵌套一层ScrollView。
在xml中添加TextView的属性:android:scrollbars =”vertical”
在代码中添加:textView.setMovementMethod(newScrollingMovementMethod())
b、利用TextView的drawableStart属性等设置icon
原始代码👇
图3.1.a
如上图3.1.a 这个界面只是为了做一个title返回用了四层布局。
修改后代码👇
图3.1.b
如上图3.1.b 这是通过TextView的drawableStart属性设置返回箭头icon,将原本四层布局减少到二层布局,效果一样。
使用接下来讲的方法,上面的代码还可以优化哦。
2)使用merge标签
如果布局的最外层和它所在的父容器控件相同那么就使用merge吧。
比如,自定义一个控件继承LinearLayout,若该自定义控件需要填充布局,填充的布局第一层是LinearLayout的话,我们就可以使用merge标签。
再比如,activity的布局文件第一层是FrameLayout的话,我们就可以使用merge标签。
图3.2.a
图3.1.b的代码修改成图3.2.a 将原本二层布局减少到一层布局,效果一样。
3)使用ViewStub标签延迟加载布局
a、ViewStub特点
ViewStub是一个不可见的,大小为0的视图,可以在运行过程中延时加载布局资源。ViewStub被设置成可见或者它的inflate()方法被调用之后填充的布局才会加载。
可以这么理解这句话:Android在绘制界面时会略过ViewStub,直到ViewStub被填充,界面再次绘制才会显现填充后的视图。
b、何时使用ViewStub?
b.1、 某个布局的显现需要条件。
比如应用第一次使用某功能、记录数据为空等等不常出现的UI需要提醒用户感知。
b.2、控制整个布局的显示与隐藏。
b.3、不需要第一时间出现在用户视野的布局。
4)约束布局ConstraintLayout
图3.4.a
如上图3.4.a 实现 关于App说明的内容,包括app图标、版本号、检查更新、新手引导、产品介绍等内容。该布局用了二层层级结构完成,如果我们改用ConstraintLayout会发什么事呢?
图3.4.b
答案是我们完全不用嵌套,一层结构即可完成布局,当然这要求我们对ConstraintLayout使用熟练。有关ConstraintLayout的用法可以参考https://www.jianshu.com/p/17ec9bd6ca8a
5)自定义View减少布局层级,优化onDraw方法
一般来说,有两点需要注意:
避免在其中进行对象的分配
使用Canvas的ClipRect方法避免过度绘制
2、减少过度绘制
什么是过度绘制,我的理解是GPU在同一区域进行了不必要的多次绘制。
项目中的过度绘制和布局层级过多一样“出色”。大部分是设置多余的背景色和不可见区域的绘制导致。
1)什么时候设置View背景以及设置在哪里?去掉不必要的背景
比如说你想让一个Activity页面呈现白色背景,你可能会这么写
直接在XML根View写上android:background=”@color/color_ffffff”,这样会导致过度绘制,为什么呢?
我们知道Activity的xml布局最终会添加到一个叫“content”的FlameLayout中,Android系统本身对这个FlameLayout设置背景颜色,如果我们在加入这个FlameLayout容器的XML根View中再设置背景颜色相当于GPU做了两次绘制,同一区域绘制了2次背景色,导致我们说的过度绘制了。
解决办法也很简单,在activity setContentView()之前写上findViewById(android.R.id.content).setBackgroundResource(R.color.color_ffffff);一行代码搞定。
2)不可见区域不要绘制,ClipRect & QuickReject
某些UI在特定操作后出现,比如刷新时背景出现,刷新后背景隐藏。
对于这种情况我们可以监听刷新操作,获取下拉距离,利用canvas的clipRect()方法只绘制规定矩形内的区域。
除了clipRect方法之外,我们还可以使用canvas的quickReject()来判断是否和某个矩形相交,从而跳过那些非矩形区域内的绘制操作。
3)去掉无用的WindowBackgroud,移除Window默认的Background
当我们使用某些主题时,系统有可能在DecorView中给我们加上一个背景,但是有时候它是无用的
@Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_overdraw);
getWindow().setBackgroundDrawable(null);
}
4)按需显示占位背景图片
3、布局复用
1)使用 <include> 标签
当我们的布局中有多个相同的布局时,可以使用include标签来进行布局的复用
4、减少布局中的View
a、使用 SpannableStringBuilder替换多个
多种不同大小、颜色或者图文混排需要显示时,我们往往会利用多个TextView来进行组合,但是某些效果通过一个TextView就可以实现。
b、使用LinearLayout自带的divider属性实现分割线,而不是在布局中手动添加一个额外的View作为分割线
与分割线相关的属性包括以下几个:
divider:传入分割线的drawable,可以是一个图片,也可以是自己通过xml实现的drawable。
showDividers:分割线显示的位置,beginning/middle/end,分割对应头部、中间、尾部。
dividerPadding:分割线距离两边的间距。
c、使用Space控件进行合理的占位
Space控件位于android.support.v4.widget包中,与一般控件不同,它的draw方法是一个空实现,因此它只占位置,而不去渲染,使用它来进行占位填充比其它控件更加高效