查找Overdraw

Overdraw(过度绘制)描述的是屏幕上的某个像素在同一帧的时间内被绘制了多次。在重叠的UI布局中,如果不可见的UI也在做绘制的操作或者后一个控件将前一个控件遮挡,会导致某些像素区域被绘制了多次,从而增加了CPU,GPU的压力。

按照以下步骤打开Show GPU Overrdraw的选项:设置 -> 开发者选项 -> 调试GPU过度绘制 -> 显示GPU过度绘制

打开调试GPU过度绘制开关之后会发现屏幕上有各种颜色,不同的颜色代表过度绘制的程度,具体如下表:

android apk优化 androidui优化_android apk优化

蓝色,淡绿,淡红,深红代表了4种不同程度的Overdraw情况,?x分别表示同一像素上同一帧的时间内被绘制了?次,1x就表示一次最理想情况,4x表示4次最差的情况,我们要做的就是尽量减少3x,4x的情况出现。

Overdraw 的处理

1、减少布局层级

使用工具Layout Inspector

按以下步骤操作:

  1. 在连接的设备或模拟器上运行应用。
  2. 点击 Tools > Android > Layout Inspector。
  3. 在出现的 Choose Process 对话框中,选择您想要检查的应用进程,然后点击 OK。

提升布局性能的关键点是尽量保持布局层级的扁平化,避免出现重复的嵌套布局。

这里介绍几种方法避免,有些用起来很简单有些确实会比较繁琐。

1)利用View的特殊属性

  • 利用TextView滑动属性来避免嵌套一层ScrollView。 

在xml中添加TextView的属性:android:scrollbars ="vertical";

在代码中添加:textView.setMovementMethod(newScrollingMovementMethod())。

  • 利用TextView的drawableStart属性等设置icon

2)使用merge标签

如果布局的最外层和它所在的父容器控件相同那么就使用merge,这样可以将原本二层布局减少到一层且效果一样。

  • 自定义一个控件继承LinearLayout,若该自定义控件需要填充布局,填充的布局第一层是LinearLayout的话,我们就可以使用merge标签。
  • activity的布局文件第一层是FrameLayout的话,我们就可以使用merge标签。

3)使用ViewStub标签延迟加载布局

ViewStub是一个宽高都为0、不可见的视图,可以在运行过程中延时加载布局资源。ViewStub被设置成可见或者它的inflate()方法被调用之后填充的布局才会加载。

何时使用ViewStub?

  • 某个布局的显现需要条件。比如应用第一次使用某功能、记录数据为空等等不常出现的UI需要提醒用户感知。
  • 控制整个布局的显示与隐藏。
  • 不需要第一时间出现在用户视野的布局。

4)约束布局ConstraintLayout

使用ConstraintLayout完全不用嵌套,一层结构即可完成布局,当然这要求我们对ConstraintLayout使用熟练。可以参考ConstraintLayout的用法。

5)自定义View减少布局层级,优化onDraw方法

一般来说,有两点需要注意:

避免在循环中进行对象的分配

使用Canvas的ClipRect方法避免过度绘制

2、减少过度绘制

GPU在同一区域进行了不必要的多次绘制。

1)去掉不必要的背景

比如想让一个Activity页面呈现白色背景,你可能会直接在XML根View写上android:background="@color/color_ffffff",这样会导致过度绘制,why?

因为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就可以实现。

Android中各种Span的用法

b、使用LinearLayout自带的divider属性实现分割线,而不是在布局中手动添加一个额外的View作为分割线

与分割线相关的属性主要有:

  • divider:传入分割线的drawable,可以是一个图片,也可以是自己通过xml实现的drawable。
  • showDividers:分割线显示的位置,beginning/middle/end,分割对应头部、中间、尾部。
  • dividerPadding:分割线距离两边的间距。

c、使用Space控件进行合理的占位

Space控件位于android.support.v4.widget包中,与一般控件不同,它的draw方法是一个空实现,因此它只占位置,而不去渲染,使用它来进行占位填充比其它控件更加高效