Android 提高app流畅性 -1

本文深入探讨了影响Android应用流畅性的关键因素,包括View布局、主线程任务、内存管理及网络请求。通过分析View渲染机制,提出了减少过度绘制、优化GPU渲染时间和避免主线程阻塞的策略。文中介绍了多种实用工具,如ShowGPUOverdraw和ProfileGPURendering,帮助开发者诊断和解决性能瓶颈。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

影响流畅性的因素有很多,比如:

  • View布局及本身;
  • 主线程任务过多,导致绘制任务延迟;
  • 内存问题影响绘制;
  • 弱网,数据请求过慢,图片加载过慢。

本文从View渲染的角度分析和优化,后续会推出其他几个角度的分析。

流畅性的概念

App运行是否流畅的唯一标准就是app是否达到每秒60帧的平滑的帧速(16ms的帧速)。

16ms是个什么概念?

16ms很长
  • 1s=10^3ms
  • 而CPU主频按单位GHz算,1秒钟有10^9次方个时钟周期

也就是说:16ms内可以执行一千多万条指令,一条指令往往要执行多个周期实际上这个值要小很多,这里不严谨的计算想告诉大家16ms是一段很长的时间。

16ms也很短

在这16ms里,必须要完成主线程里面的所有任务,包含各类事件的响应、业务逻辑的处理、垃圾回收、UI绘制…先说重中之重的界面绘制。

  1. 硬件时钟定时发出VSYNC信号:(Refresh Rate:这取决于硬件的固定参数,例如60Hz。)
  2. 栅格化:CPU拿到UI对象xml文件处理为多维图形,纹理 —–通过OpeGL ES接口调用GPU
  3. 光栅化:GPU对图进行光栅化(Frame Rate:代表了GPU在一秒内绘制操作的帧数,例如30fps,60fps )
  4. 投射到屏幕
栅格化

通常来说栅格化就是view的measure、layout再处理成多边形和纹理的过程,这部分是整个流程中最耗时的,在可见性不发生改变的时候这个过程是可以跳过的。
在这里插入图片描述

在某个View第一次需要被渲染时,DisplayList会因此而被创建,当这个View要显示到屏幕上时,我们会执行GPU的绘制指令来进行渲染。

  • 如果你在后续有执行类似移动这个View的位置等操作而需要再次渲染这个View时,我们就仅仅需要额外操作一次渲染指令就够了。
  • 如果你修改了View中的某些可见组件,那么之前的DisplayList就无法继续使用了,我们需要回头重新创建一个DisplayList并且重新执行渲染指令并更新到屏幕上。

GPU会获取图形数据进行渲染,然后硬件负责把渲染后的内容呈现到屏幕上,他们两者不停的进行协作。当系统收到VSYNC信号的时候无法进行正常渲染,这样就发生了丢帧现象。那么用户在32ms内看到的会是同一帧画面。这样的情况多了,用户就会觉得应用程序超级烂。


下面进入到干货部分,主要从工具和理论两个方面来说明。

工具篇

1. 过度绘制工具 (Show GPU Overdraw)

通过手机设置里面的开发者选项,打开调试GPU过度绘制工具 (Show GPU Overdraw)的选项,可以观察UI上的Overdraw情况。
在这里插入图片描述

工具解读
  • 蓝色 这部分区域绘制了一次
  • 淡绿 这部分区域绘制了两次
  • 淡红 这部分区域绘制了三次
  • 深红 这部分区域绘制了三次

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

Overdraw有时候是因为你的UI布局存在大量重叠的部分,还有的时候是因为非必须的重叠背景。例如某个Activity有一个背景,然后里面的Layout又有自己的背景,同时子View又分别有自己的背景。仅仅是通过移除非必须的背景图片,这就能够减少大量的红色Overdraw区域,增加蓝色区域的占比。这一措施能够显著提升程序性能。

2. GPU 呈现模式分析 (Profile GPU Rendering)

通过手机设置里面的开发者选项,打开手机里面的开发者选项,选择GPU 呈现模式分析 (Profile GPU Rendering),选中在屏幕上显示条形图(On screen as bars)的选项。

AS 2.x 的版本应该也是有这个工具的,但是3.0 改成profile之后就没了。
在这里插入图片描述

工具解读 (6.0以上的系统工具)

随着界面的刷新,界面上会滚动显示垂直的柱状图来表示每帧画面所需要渲染的时间,柱状图越高表示花费的渲染时间越长。

中间有一根绿色的横线,代表16ms,我们需要确保每一帧花费的总时间都低于这条横线,这样才能够避免出现卡顿的问题。

每一条柱状线都包含三部分,蓝色代表测量绘制Display List的时间,红色代表OpenGL渲染Display List所需要的时间,黄色代表CPU等待GPU处理的时间。

竖条区段渲染阶段说明
橙色交换缓冲区表示 CPU 等待 GPU 完成其工作的时间。 如果此竖条升高,则表示应用在 GPU 上执行太多工作。
红色命令问题表示 Android 的 2D 渲染器向 OpenGL 发起绘制和重新绘制显示列表的命令所花的时间。 此竖条的高度与它执行每个显示列表所花的时间的总和成正比—显示列表越多,红色条就越高。
天蓝色同步和上传表示将位图信息上传到 GPU 所花的时间。 大区段表示应用花费大量的时间加载大量图形。
蓝色绘制表示用于创建和更新视图显示列表的时间。 如果竖条的此部分很高,则表明这里可能有许多自定义视图绘制,或 onDraw 函数执行的工作很多。
绿色测量/布局表示在视图层次结构中的 onLayout 和 onMeasure 回调上所花的时间。 大区段表示此视图层次结构正在花很长时间进行处理。
绿色动画/输入处理表示评估运行该帧的所有动画程序所花的时间。 如果此区段很大,则表示您的应用可能在使用性能欠佳的自定义动画程序,或因更新属性而导致一些意料之外的工作。
绿色其他时间/VSync 延迟表示应用执行两个连续帧之间的操作所花的时间。 它可能表示界面线程中进行的处理太多,而这些处理任务本可以分流到其他线程。
导出GPU 呈现模式分析结果

使用adb命令可以导出GPU呈现的结果,可以输出最近一些帧的详细帧信息,有了他们我们就可以进行数据化的分析和优化了。

// adb shell dumpsys gfxinfo > 输出路径
// adb shell dumpsys gfxinfo 直接在控制台打印
adb shell dumpsys gfxinfo > test.txt

输出结果

// 输出结果
com.zhy.sample_okhttp/com.zhy.sample_okhttp.MainActivity/android.view.ViewRootImpl@e7194d7 (visibility=0)
	Draw	Prepare	Process	Execute
	50.00	0.69	22.25	12.80
	50.00	0.51	2.36	8.44
	2.84	0.29	4.39	15.26
	
Stats since: 3115548352260ns
Total frames rendered: 92
Janky frames: 11 (11.96%)
90th percentile: 21ms
95th percentile: 61ms
99th percentile: 129ms
Number Missed Vsync: 3
Number High input latency: 0
Number Slow UI thread: 7
Number Slow bitmap uploads: 0
Number Slow issue draw commands: 8

View hierarchy:
  com.zhy.sample_okhttp/com.zhy.sample_okhttp.MainActivity/android.view.ViewRootImpl@e7194d7
  29 views, 42.37 kB of display lists


Total ViewRootImpl: 1
Total Views:        29
Total DisplayList:  42.37 kB

这里将逐一解释以上重点信息:

  • Draw:测量绘制Display List的时间,也就是measure和layout的时间。
  • Prepare:标示准备时间
  • Process:OpenGL渲染Display的时间
  • Execute:CPU等待GPU处理的时间,主线程被其他任务占用的时间,比如:处理业务逻辑之类的

  • Graphics info for pid 31148 [com.android.settings]: 表明当前dump的为设置界面的帧信息,pid为31148
  • Total frames rendered: 105 本次dump搜集了105帧的信息
  • Janky frames: 2 (1.90%) 105帧中有2帧的耗时超过了16ms,卡顿概率为1.9%
  • Number Missed Vsync: 0 垂直同步失败的帧
  • Number High input latency: 0 处理input时间超时的帧数
  • Number Slow UI thread: 2 因UI线程上的工作导致超时的帧数
  • Number Slow bitmap uploads: 0 因bitmap的加载耗时的帧数
  • Number Slow issue draw commands: 1 因绘制导致耗时的帧数
  • HISTOGRAM: 5ms=78 6ms=16 7ms=4 … 直方图数据,表面耗时为0-5ms的帧数为78,耗时为5-6ms的帧数为16,同理类推。

HierarchView (已删除)

Hierarchy Viewer是随Android SDK发布的工具,位于Android SDK/tools/hierarchyviewer.bat,这个工具已经被删除很久了,而且测试时候需要root权限,不建议大家去深挖。这个工具最大的优点在于可以显示每一个视图Measure、layout、draw的时间,如果需要深度优化的时候可以用这个工具分析一下。

ps: HierarchView话说被AS的Layout Inspector工具所替代,事实上Layout Inspector只能用来检查层级和位置。

在这里插入图片描述

工具解读

在Hierarchy Viewer窗口中,你可以看到实际的运行的速度,所有的子View上面都有了3个圈圈。这三个圈圈依次代表:

  • measure
  • layout
  • draw的

(取色范围为红、黄、绿色),如果你发现某个View上的圈是红色,那么说明这个View相对其他的View,该操作运行最慢,注意只是相对别的View,并不是说就一定很慢。

理论

优化过度绘制:

  1. 移除不必要的布局嵌套,减少深度。
  2. 使用merge替换include,这样在被引入时顶结点会自动被忽略,而将其子节点全部合并到主布局中.
  3. 使用ViewStub来替换visible=gone/visible的切换,ViewStub 标签最大的优点是当你需要时才会加载,使用它并不会影响UI初始化时的性能。要注意的是,使用时候如果两次初始化会引发一个异常,所以使用前先判断是否被加载过。
  4. 移除不必要的背景
  5. 在自定义view的onDraw中,如果涉及到重叠的绘制view时,可以考虑利用局部绘制避免过度绘制。

优化绘制时间:

这个问题的难度主要还是在定位上,由于HierarchView已经被删除,而GPU Rendering只能是起到一个指导的作用。这个就得用到另外一个工具systrace了,这个我将会放到下一个篇章来讲。

参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值