上篇介绍了android界面渲染主要是Display的过程,只要在一个时间段也就是16ms中,CPU和GPU不能正常处理完数据就会产生卡顿.
而
- CPU(中央处理器) :多缓存多分支,适用于复杂的逻辑运算,主要负责Measure,Layout,Record,Execute的计算操作
- GPU(图像处理器):众核少缓存,适用于结构单一的数据处理,主要负责Rasterization(栅格化)操作
实际上最后还是回到Measure,Layout,和draw上.也就是说上述三个步骤中的某一个步骤出现了耗时较长的操作,就容易导致界面卡顿。一般来说多发生在draw上。
过度绘制(Overdraw)
什么是过度绘制?
Overdraw(过度绘制)描述的是屏幕上的某个像素在同一帧的时间内被绘制了多次。在多层次重叠的UI结构里面,如果不可见的UI也在做绘制的操作,会导致某些像素区域被绘制了多次。这样就会浪费大量的CPU以及GPU资源。
如何检测过度绘制
开发者选项->调试GPU过度绘制->显示过度绘制区域�
蓝色,淡绿,淡红,深红代表了4种不同程度的Overdraw情况,我们的目标就是尽量减少红色Overdraw,看到更多的蓝色区域
如何优化过度绘制
移除布局中多余的背景
layout层级扁平化,或者该用约束布局(ConstraintLayout)
减少透明度的使用
移除Window默认的Background
通常,我们使用的theme
都会包含了一个windowBackground
,比如theme
的如下:
<item name="android:windowBackground">@color/background_material_light</item>
然后通常我们自己的layout
下,会另外加一层背景,这就会引起过度绘制,
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/white"
android:orientation="vertical">
一般的解决方式把theme
下的Background
移除
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="android:windowBackground">@null</item>
</style>
或者在Activity中的onCreate()下添加
getWindow().setBackgroundDrawable(null);
去除子控件的背景
在开发中,经常为了一些视觉效果,需要给子控件附加背景.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/white"
android:orientation="vertical">
<EditText
...
android:background="@drawable/alert_edit_bg1"
... />
<LinearLayout/>
如果按照过度绘制的思想来看这个问题的话,那这里也会村子啊过度绘制的情况,最合理的设想是如果子view有背景,并且跟父view背景不同,那么应该移除父view的背景,保留子view的背景,这样就能避免过度绘制了。
不过如果真的严格按照这个逻辑来写代码的话,会导致多写很多代码.况且以目前手机的性能来讲,x1级别的过度绘制不太会影响到性能。
将layout层级扁平化
使用Layout Inspector去查看layout的层次结构
老版本Android SDK提供的是一个Hierarchy Viewer
工具,可以用来分析布局深度,
但是在Android studio3.1之后,换成了Layout Inspector
,入口在Tools-->Layout Inspector
使用嵌套少的布局
相同层级下,Fragment的效率最高,其次是LinearLayout
LinearLayout对比RelativeLayout
-
LinearLayout
在没有设置weight属性的时候,只会对子视图进行一次measure
,而RelativeLayout
会在水平方向和竖直方向分别进行一次measure
- 另外,系统的
View
的measure
方法里对绘制过程做了一个优化,如果我们或者我们的子View没有要求强制刷新,而父View给子View的传入值也没有变化(也就是说子View的位置没变化),就不会做无谓的measure
。但是RelativeLayout
会做两次measur
e,而在做横向的测量时,纵向的测量结果尚未完成,只好暂时使用myHeight
传入子View
系统,假如子View
的Height
不等于(设置了margin
)myHeight
的高度,那么measure中上面代码所做得优化将不起作用,所以尽量使用padding代替margin
RelativeLayout和LinearLayout性能比较
使用抽象布局 include、merge 、ViewStub
include、merge 、ViewStub这三个布局都是官方提供的用于布局优化的
布局重用 include
标签复用,比起性能上的优化,更大的作用我感觉在xml文件的优化上
减少布局层级 merge
<merge/>多用于替换FrameLayout或者当一个布局包含另一个时,<merge/>标签消除视图层次结构中多余的视图组。
如果xml的根部局是
FrameLayout
,直接替换成merge
就能减少一个布局深度。如果你的主布局文件是垂直布局,引入了一个垂直布局的
include
,这个时候就可以将include
的根布局替换成merge
延迟加载 ViewStub
ViewStub
是一个不可见的,大小为0的View
,能为xml加载减小不少压力。常用于 进度条、显示错误消息等不需要第一时间显示的视图,inflate()
之后会被对应的layout所代替
注:ViewStub目前有个缺陷就是还不支持 <merge /> 标签。
使用lint来优化布局的层次结构
Analyze->Inspect code
布局性能方面的信息位于Android
> Lint
> Performance
下,我们可以点开它来看下一些优化建议。
下面是lint的一些优化技巧:
- 使用复合图片
如果一个线性布局中包含一个ImageView
和一个TextView
,可以使用复合图片来替换掉 - 合并根节点
如果一个FrameLayout
是整个布局的根节点,并且也没有提供背景、留白等等,那么可以使用<merge>
标签来替换掉,因为DecorView
本身就是一个FrameLayout
。 - 移除布局中无用的叶子
布局是一个树形的结构,如果一个布局没有子View
或者背景,那么可以把它移除掉(这布局本身就不可见了)。 - 移除无用的父布局
如果一个布局没有兄弟,也不是ScrollView
或者根View
,并且也没有背景,那么可以把这个父布局移除掉,然后把它的子view
移到它的父布局下。 - 避免过深的层次结构
过多的布局嵌套不利于性能,可以使用更扁平化的布局,如RelativeLayout
、GridLayout
、ConstraintLayout
等布局来提高性能。布局默认的最大深度为10
减少透明度的使用
对于不透明的view
,只需要渲染一次即可把它显示出来。但是如果这个view
设置了alpha
值,则至少需要渲染两次。这是因为使用了alpha
的view
需要先知道混合view
的下一层元素是什么,然后再结合上层的view
进行Blend混色处理。透明动画、淡入淡出和阴影等效果都涉及到某种透明度,这就会造成了过度绘制。可以通过减少渲染这些透明对象来改善过度绘制。比如:在TextView
上设置带透明度alpha
值的黑色文本可以实现灰色的效果。但是,直接通过设置灰色的话能够获得更好的性能。
减少自定义View的过度绘制,使用clipRect()和QuickReject()
-
clipRect():可以指定一块矩形区域,只有在这个区域内才会被绘制,其他的区域会被忽视,可以有效的节约CPU与GPU的资源
canvas.clipRect(0, 0,200,200); //指定(0,0)到(200,200)的矩形区域为绘制区域
QuickReject():判断画布是否与你指定的矩形相交,借此来决定是否需要绘制该画布
boolean skipDraw=canvas.quickReject(new RectF(), Canvas.EdgeType.AA);
onDraw()中不要创建新的局部变量以及不要做耗时操作
onDraw()
中不要创建新的局部变量,因为onDraw()
方法可能会被频繁调用,大量的临时对象会导致内存抖动,会造成频繁的GC,从而使UI线程被频繁阻塞,导致画面卡顿。这种问题一般android studio的lint静态检查都会报警告,平时多注意些android studio标黄的警告,有助于性能调优
尝试使用ConstraintLayout
-
较高的性能优势
比起LinearLayout 、RelativeLayout等,ConstraintLayout可以用更少的布局层级来达到相同的效果
-
完美的屏幕适配
ConstraintLayout的大小、距离都可以使用比例来设置,所以其适配性更好。
当然不是任何时候都推荐使用ConstraintLayout
的,在简单的布局下还是建议使用LinearLayout
、RelativeLayout
,因为对于相同层级的布局下,ConstraintLayout
的measure,layout,draw等过程会显得更加重.