Android 性能优化2 - 绘制优化

一、说明

说起绘制优化,那么什么是绘制呢 ?

例如动画片,其实每一个动作都是很多张静态的图组合起来的,当不停的切换图的时候,就被串联成了走、跑、打斗等动画。对比我们的 App 也是,每一个滑动的动效都是若干个静态的图片(帧)组合起来不停变换组成的。 如果播放的时候没有及时的变换图片(帧)就会感觉到卡顿。

而动画与手机应用的不同点在于动画片是提前做好再拿出来播放的,而应用的动画动效是需要动态生成的,这样就需要大量的计算,如果太过复杂的话就会造成在短时间之内画不完,也就造成了卡顿。
在这里插入图片描述

二、卡顿原理

首先了解一个概念:
FPS:每秒传递的帧数。

在理想情况下,60 FPS 就感觉不到卡(每秒刷新60帧),这也意味着绘制时长应该在16ms以内(1000ms / 60帧 ≈ 16ms)。
如果有一些复杂的绘制无法在16ms以内完成,例如一个绘制操作是在24ms完成就会发生丢帧现象,它在32ms的时候才会进行渲染,这样在32ms看到的会是同一帧,也就造成了卡顿。

三、造成卡顿的原因

原因1:界面绘制

绘制任务太重,绘制一帧内容耗时太长,是造成卡顿的因素。具体来说,绘制的层级太深、页面太复杂、刷新不合理这些都会造成卡顿。

原因2:数据处理

  1. 数据处理在 UI 线程:如果把耗时的数据处理放到 UI 线程就会导致 UI 线程长时间等待,也就会造成卡顿。
  2. 数据处理占用 CPU 高:虽然我们会把耗时的数据处理放到异步线程,但如果数据处理开了多个线程,而且计算量巨大的话,依然会给 CPU 造成很大的负担,从而导致主线程拿不到时间片。
  3. 内存增加导致 GC 频繁:GC 的时候有时会 “Stop World”,也就是所有的计算都停止,待 GC 完成后再进行运算,如果频繁的 “Stop World” 就会非常影响性能,从而导致卡顿。

四、优化

上面我们了解到卡顿的原理和造成卡顿的原因,接下来说一下怎样去优化从而避免卡顿。

1、布局优化

  1. 减少代码层级。
  2. 减少同一层级控件数量。
  3. 一个控件的属性越少,解析越快,删除控件中的无用属性。
  4. 如果层级相同 LinearLayout 要优于 RelativeLayout,因为 RelativeLayout 会对子 View 做2次测量。
  5. 如果 LinearLayout 有 weight 属性,也需要2次测量,但由于没有依赖关系,仍然比 RelativeLayout 的效率高。
  6. ViewStub 提高显示速度,如果某个布局中多个元素可能只会显示一部分,而另一部分不会显示,应该选用 ViwStub,它可以实现延迟加载布局。
  7. 尽量少用 wrap_content,它会增加布局的计算成本,如果已知宽度为固定值时,尽量写固定值。
  8. 使用 标签减少布局嵌套层级。

2、减少主线程耗时占用

主线程的职责是处理用户交互,在屏幕上绘制像素,并进行加载显示相关的数据,在Android开发中,特别需要避免任何阻碍主线程的事情,这样应用才能保持对用户操作的即时响应。

在实际的开发过程中,总结起来主线程主要做以下几个方面的工作:

  • UI生命周期
  • 系统事件处理
  • 消息处理
  • 界面布局、绘制、刷新

除此以外,尽量避免放到主线程中,特别是复杂的数据计算和网络请求。

3、避免过度绘制

什么是过度绘制

过度绘制(Overdraw)是指在屏幕上的某个像素在同一帧的时间内被绘制了多次。在多层次重叠的UI结构(如带背景的 TextView)中,如果不可见的 UI 也在做绘制的操作,就会导致某些像素区域被绘制了多次,从而浪费多余的 CPU 以及 GPU 资源。

原因

  • XML 布局:控件有重叠且都有设置背景
  • View 自绘:View.OnDraw 里面同一个区域被绘制多次

解决方法:

方法1:减少重叠部分背景设置

XML 布局的时候尽可能减少为重叠部分都设置背影色(完全杜绝是不太可能的,我们有针对性的注意减少就行)。

兴个例子:
子 View 的宽度都是 match_parent 的,这个时候你需要衡量是否需要为 父 View 设置一个背景色。

方法2:自定义 View onDraw 方法

自定义View 中 onDraw 注意同一个区域绘制次数。

方法3:去掉 window 的默认背景

使用 Android 自带的主题时,window 会被默认添加背景色,当我们布局时再设置 background 属性时,window 默认的背景色就没什么用了,而且设置2次背景色也会导致 Overdraw,从而带来性能损耗。

有2个方法可以去掉 window 自带的背影色:

  • 在 Activity onCreate() 中 setContentView() 之后调用 getWindow().setBackgroundDrawable(null);
  • 在 theme 中添加 android:windowbackground=“null”;
方法4:ClipRect & QuickReject

虽然自定义 View 减少了 Layout 的层级,但在实际绘制时也是会过度绘制的。原因是有些过于复杂的自定义 View(通常重写了onDraw方法),Android 系统无法检测在 onDraw 中具体会执行什么操作,无法监控并自动优化,也就无法避免 Overdraw 了。

解决方法:

  • 在自定义 View 中可以通过 canvas.clipRect()来帮助系统识别那些可见的区域。这个方法可以指定一块矩形区域,只有在这个区域内才会被绘制,其他的区域会被忽视。它可以节约CPU和GPU资源。
  • 可以使用 canvas.quickreject()来判断是否没和某个矩形相交,从而跳过那些非矩形区域内的绘制操作。

五、检测

上面介绍了一些卡顿的原因和一些优化的方法,现在介绍一些检测的工具。

卡顿检测:Profile GPU Rendering

打开方式:

  1. 系统设置
  2. 开发者选项
  3. Profile GPU Rendering
    在这里插入图片描述
    呈现解释:
  • 蓝色:代表测量绘制的时间,它对应视图的 onDraw 函数,如果该颜色很高,表示 onDraw 函数处理的事情太多。
  • 红色:代表执行2D渲染 Display List 的时间。如果该颜色较高,可能是重新提交了视图导致。
  • 橙色:CPU 告诉 GPU 渲染一帧的地方,这是一个阻塞调用,因为 CPU 会一直等待 GPU 发出命令的回复,如果很高,表示 GPU 太繁忙。
  • 紫色:表示将资源转移到渲染线程的时间,只有4.0及以上的版本才会提供。

函数耗时监控:TraceView

作用:它可以分析具体某个方法耗时的长短。

使用的时候首先需要得到一个 .trace 的文件,然后分析文件中的信息。

注意:在新版本中谷歌已经废弃这一工具,并推荐使用 CPU Profiler,这个更加强大和方便。

UI 性能检测:Systrace

在应用程序开发过程中,UI(用户界面)的流畅度是体验的核心,特别是在动画、跳转或者列表的滑动过程中,出现卡顿和无响应是非常影响用户体验的,要解决这些问题,首先要找到问题的原因,Systrace 就是来帮你解决这个问题的。

作用:

  • 跟踪系统的 I/O 操作、内核工作队列、CPU 负载等
  • 在 UI 显示性能分析上提供很好的数据,特别是在动画播放不流畅、渲染卡等问题上。
  • 跟踪、收集、检查定时信息,可以很直观地查看 CPU 周期消耗的具体时间,显示每个线程和进程的跟踪信息,使用不同颜色来突出问题的严重性,并提供如何解决这些问题的建议。

这个与 TraceView 一样,在新版本中已经废弃,并推荐使用 CPU Profiler

Layout 层级和绘制时间检测:Hierarchy Viewer

Hierarchy Viewer 是 Android SDK 自带的一款可视化调试工具,用来检查 Layout 嵌套及绘制时间,以可视化的布局角度直观获取 Layout 布局设计和各种属性信息,开发者在调试和布局UI界面时可以很方便地使用,提高用户的开发效率。

打开方式:

  1. 菜单栏
  2. Tools
  3. Android
  4. Android Device Monitor

在这里插入图片描述
在这里插入图片描述
view表示这个控件是这个树下的最后一个控件,即表示是它本身,下面的时间表示 Measure、Layout 以及 Draw 三个阶段的耗时。最后一个框有不同色的三个指示灯,分别对应当前控件在测量、布局以及画视图三个阶段,颜色表示这个控件占用的时间百分比,如果是绿色的,表示该控件在该阶段比其他50%的控件的速度要快,黄色表示比其他50%的控件的速度要慢,红色表示该控件在该阶段的处理速度是最慢的,就需要注意了。

注意:在新版本的 SDK 中,谷歌已经废弃了这一工具,如果是用 Android Studio 3.1 或以上版本的话,谷歌推荐使用 Layout Inspector,这个用法就不详细说了,大家可以参考官方文档

过度绘制检测:Show GPU Overdraw

打开方法:

  1. 系统设置
  2. 开发者选项
  3. 显示 GPU 过度重绘

在设置时,如果有App已经打开,需要终止App进程,重新启动。

在这里插入图片描述
打开后可以根据不同的颜色观察 UI 上的 Overdraw 情况,蓝色、淡绿、淡红、深红代表4种不同程度的 Overdraw 情况。

  • 无色:没有过度绘制,每个像素绘制了1次。
  • 蓝色:每个像素多绘制了1次。大片的蓝色还是可以接受的。如果整个窗口是蓝色的,可以尝试优化减少一次绘制。
  • 绿色:每个像素多绘制了2次。
  • 淡红:每个像素多绘制了3次。一般来说,这个区域不超过屏幕的1/4是可以接受的。
  • 深红:每个像素多绘制了4次或者更多。严重影响性能,需要优化,避免深红色区域。

我们的目标是尽量减少红色 Overdraw,看到更多的蓝色区域。

六、总结

在该篇文章中分析了卡顿原理、造成卡顿的原因、优化方法和性能检测工具,希望能对大家优化绘制性能有所帮助。

七、推荐

  1. Android 性能优化1 - 启动优化
  2. Android 性能优化2 - 绘制优化
  3. Android 性能优化3 - 解决内存泄露
  4. Android 内存优化4 - 图片优化
  5. Android 性能优化5 - 内存优化
  6. Android 性能优化6 - Hybrid 应用启动优化
发布了179 篇原创文章 · 获赞 91 · 访问量 33万+

猜你喜欢

转载自blog.csdn.net/haha223545/article/details/95090699