Android 界面过度绘制优化

过度绘制(Overdraw)的概念

过度绘制(Overdraw)描述的是屏幕上的某个像素在同一帧的时间内被绘制了多次。在多层次重叠的 UI 结构里面,如果不可见的 UI 也在做绘制的操作,会导致某些像素区域被绘制了多次,同时也会浪费大量的 CPU 以及 GPU 资源。

     在 Android 手机的开发者选项中,有一个『调试 GPU 过度绘制』的选项,该选项开启之后,手机显示如下,显示出来的蓝色、绿色的色块就是过度绘制信息。

 也可以使用adb命令打开GPU过渡绘制:

开启『调试 GPU 过度绘制』:adb shell setprop debug.hwui.overdraw show

关闭『调试 GPU 过度绘制』:adb shell setprop debug.hwui.overdraw false

比如上面界面中的『调试 GPU 过度绘制 』的那个文本显示为蓝色,表示其过度绘制了一次,因为背景是白色的,然后文字是黑色的,导致文字所在的区域就会被绘制两次:一次是背景,一次是文字,所以就产生了过度重绘。

在官网的 Debug GPU Overdraw Walkthrough 说明中对过度重绘做了简单的介绍,其中屏幕上显示不同色块的具体含义如下所示:

每个颜色的说明如下:

  • 原色:没有过度绘制
  • 蓝色:1 次过度绘制
  • 绿色:2 次过度绘制
  • 粉色:3 次过度绘制
  • 红色:4 次及以上过度绘制

过度绘制的存在会导致界面显示时浪费不必要的资源去渲染看不见的背景,或者对某些像素区域多次绘制,就会导致界面加载或者滑动时的不流畅、掉帧,对于用户体验来说就是 App 特别的卡顿。为了提升用户体验,提升应用的流畅性,优化过度绘制的工作还是很有必要做的。

优化原则

  • 一些过度绘制是无法避免的,比如之前说的文字和背景导致的过度绘制,这种是无法避免的。
  • 应用界面中,应该尽可能地将过度绘制控制为 2 次(绿色)及其以下,原色和蓝色是最理想的。
  • 粉色和红色应该尽可能避免,在实际项目中避免不了时,应该尽可能减少粉色和红色区域。
  • 不允许存在面积超过屏幕 1/4 区域的 3 次(淡红色区域)及其以上过度绘制。

优化方法

1)移除布局中不需要的背景
2)将layout层级扁平化
3)减少透明度的使用

1.移除默认的 Window 背景

       一般应用默认继承的主题都会有一个默认的 windowBackground ,比如默认的 Light 主题:

       <style name="Theme.Light">
    <item name="isLightTheme">true</item>
    <item name="windowBackground">@drawable/screen_background_selector_light</item>
    ...
</style>

但是一般界面都会自己设置界面的背景颜色或者列表页则由 item 的背景来决定,所以默认的 Window 背景基本用不上,如果不移除就会导致所有界面都多 1 次绘制。

可以在应用的主题中添加如下的一行属性来移除默认的 Window 背景:

<item name="android:windowBackground">@android:color/transparent</item>
<!-- 或者 -->
<item name="android:windowBackground">@null</item>

或者在 BaseActivityonCreate() 方法中使用下面的代码移除:

getWindow().setBackgroundDrawable(null);
// 或者
getWindow().setBackgroundDrawableResource(android.R.color.transparent);

2.移除不必要的背景

         比较常见的可优化场景:ViewPager 加多个 Fragment 组成的首页界面,如果你的每个 Fragment 都设置有背景色的话, 你就可以不用给 Activity 的根布局设置背景,如果你还给 ViewPager 还设置了背景,那个这个背景是没必要的,同样可以移除。

3.将layout层级扁平化

        往往我们在写界面的时候都会使用基本布局来实现,这可能会出现一些性能问题。比如:使用嵌套的LinearLayout可能会导致布局的层次结构变得过深。另外,如果在LinearLayout中使用了layout_weight的话,那么他的每一个子 view都需要测量两次。特别是用在 ListView 和 GridView 时,他们会被反复测量。

布局嵌套过多的话会导致过度绘制,从而降低性能,因此我们需要将布局的层次结构尽量扁平化。

3.1使用Layout Inspector去查看layout的层次结构

         在Android Studio中点击Tools > Android > Layout Inspector。然后在出现的 Choose Process 对话框中,选择想要检查的应用进程即可。

Layout Inspector会自动捕获快照,然后会显示以下内容:

  • View Tree:视图在布局中的层次结构。

  • Screenshot:每个视图可视边界的设备屏幕截图。

  • Properties Table:选定视图的布局属性。

通过左侧View Tree即可看到布局中的层次结构 

3.1使用嵌套少的布局

3.2使用merge标签减少嵌套

通过<include>标签能够复用布局。

比如,我们要复用如下的一个布局,一个垂直的线性布局包含一个ImageViewTextView,其布局文件layout_include.xml如下:

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="vertical">
    <ImageView
        ...
        />
    <TextView
        ...
        />
</LinearLayout>

然后我们就可以通过<include>来复用这个布局了,其布局文件activity_include.xml如下:

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#fff"
    android:orientation="vertical">
    <include layout="@layout/layout_include"/>
    <include layout="@layout/layout_include"/>
    <include layout="@layout/layout_include"/>
</LinearLayout>

但是上面这个例子会有个问题:其父布局是垂直的线性布局,include进来的也是垂直的线性布局,这就会造成了布局嵌套,而且这种嵌套是没必要的,那么就可以使用<merge>标签来减少这种嵌套。将layout_include.xml改成以下即可:

<merge
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content">
    <ImageView
        ...
        />
    <TextView
        ...
        />
</merge>

3.3 使用lint来优化布局的层次结构

lint是一个静态代码分析工具,可以用来协助优化布局的性能。要使用lint,点击AnalyzeInspect Code即可,如下图所示:

inspec Code

 布局性能方面的信息位于AndroidLintPerformance下,我们可以点开它来看下一些优化建议。

4.减少透明度的使用

对于不透明的view,只需要渲染一次即可把它显示出来。但是如果这个view设置了alpha值,则至少需要渲染两次。这是因为使用了alphaview需要先知道混合view的下一层元素是什么,然后再结合上层的view进行Blend混色处理。透明动画、淡入淡出和阴影等效果都涉及到某种透明度,这就会造成了过度绘制。可以通过减少渲染这些透明对象来改善过度绘制。比如:在TextView上设置带透明度alpha值的黑色文本可以实现灰色的效果。但是,直接通过设置灰色的话能够获得更好的性能。

5.减少自定义View的过度绘制,使用clipRect()

6.使用ViewStub标签延迟加载

在项目中,有些复杂的布局很少使用到,比如进度指示器等等。那么我们可以通过<ViewStub>标签来实现在需要时才加载布局。使用<ViewStub>能够减少内存的使用并且加快渲染速度。

 

猜你喜欢

转载自blog.csdn.net/JustinNick/article/details/82492659