Android专项测试之(卡顿)
卡顿分析
卡顿
❖ 场景
❖ 界⾯加载
❖ 滑动
❖ 数据解释
❖ ⼈眼感知的流畅度不能⼩于 60fps
卡顿影响因素
❖ 内存问题:(内存抖动、full gc)
❖ cpu (计算耗时)
❖ render (布局复杂、overdraw)
渲染性能
先启用开发者选项。然后按以下步骤操作:
1、在您的设备上,转到 Settings 并点按 Developer Options。
2、向下滚动到硬件加速渲染部分,并选择调试 GPU 过度绘制。
3、在调试 GPU 过度绘制对话框中,选择显示过度绘制区域。
Android 将按如下方式为界面元素着色,以确定过度绘制的次数:
真彩色:没有过度绘制
蓝色:过度绘制 1 次
绿色:过度绘制 2 次
粉色:过度绘制 3 次
红色:过度绘制 4 次或更多次
GPU渲染分析
⼀帧超过16ms不代表卡顿
adb shell dumpsys gfxinfo |less
Total frames rendered: 43
Janky frames: 8 (18.60%)
50th percentile: 5ms
90th percentile: 32ms
95th percentile: 40ms
99th percentile: 150ms
adb shell dumpsys gfxinfo com.bs.finance
使用systrace分析
官方文档
systrace帮助文档
全局选项
全局选项 | 说明 |
---|---|
-h | --help | 显示帮助消息。 |
-l | --list-categories | 列出您的已连接设备可用的跟踪类别。 |
命令和命令选项
命令和选项 | 说明 |
---|---|
-o file | 将 HTML 跟踪报告写入指定的文件。如果您未指定此选项,systrace 会将报告保存到 systrace.py 所在的目录中,并将其命名为 trace.html。 |
-t N | --time=N | 跟踪设备活动 N 秒。如果您未指定此选项,systrace 会提示您在命令行中按 Enter 键结束跟踪。 |
-b N | --buf-size=N | 使用 N KB 的跟踪缓冲区大小。使用此选项,您可以限制跟踪期间收集到的数据的总大小。 |
-k functions | --ktrace=functions | 跟踪逗号分隔列表中指定的特定内核函数的活动。 |
-a app-name | --app=app-name | 启用对应用的跟踪,指定为包含进程名称的逗号分隔列表。这些应用必须包含 Trace 类中的跟踪检测调用。您应在分析应用时指定此选项。很多库(例如 RecyclerView)都包括跟踪检测调用,这些调用可在您启用应用级跟踪时提供有用的信息。如需了解详情,请参阅定义自定义事件。 如需跟踪搭载 Android 9(API 级别 28)或更高版本的设备上的所有应用,请传递用添加引号的通配符字符 “*”。 |
--from-file=file-path | 根据文件(例如包含原始跟踪数据的 TXT 文件)创建交互式 HTML 报告,而不是运行实时跟踪。 |
-e device-serial | --serial=device-serial | 在已连接的特定设备(由对应的设备序列号标识)上进行跟踪。 |
categories | 包含您指定的系统进程的跟踪信息,如 gfx 表示用于渲染图形的系统进程。您可以使用 -l 命令运行 systrace,以查看已连接设备可用的服务列表。 |
python 2.7.x only
❖ ./systrace.py -l 列举所有⽀持的性能数据分类
❖ ./systrace.py -t 10 收集10s左右的系统性能数据
❖ ./systrace.py -t 10 -j 保存⼀份json数据⽂件⽤于编程分析
操作步骤:
1、用python2执行systrace.py文件,遇到问题找问题1、2
2、操作手机要检测的页面
3、按Enter,生产测试报告
D:\Python27>python.exe D:\Android-SDK\platform-tools\systrace\systrace.py
Agent cgroup_data not started.
These categories are unavailable: memory workq
Warning: Only 2 of 3 tracing agents started.
Starting tracing (stop with enter) # 出现这步时,就可以操作手机,操作完成后,按Enter
Tracing completed. Collecting output...
Outputting Systrace results...
Tracing complete, writing results
Wrote trace HTML file: file://D:\Python27\trace.html
D:\Python27>
浏览器打开file://D:\Python27\trace.html
每个条形堆上方的多色线条表示特定线程随时间变化的一组状态。每段线条可以包含以下颜色之一:
绿色:正在运行
线程正在完成与某个进程相关的工作或正在响应中断。
蓝色:可运行
线程可以运行但目前未进行调度。
白色:休眠
线程没有可执行的任务,可能是因为线程在遇到斥锁定时被阻止。
橙色:不可中断的休眠
线程在遇到 I/O 操作时被阻止或正在等待磁盘操作完成。
紫色:可中断的休眠
线程在遇到另一项内核操作(通常是内存管理)时被阻止。
键盘快捷键
下表列出了查看 Systrace 报告时可以使用的键盘快捷键:
键 | 说明 |
---|---|
W | 放大跟踪时间轴。 |
A | 在跟踪时间轴上向左平移。 |
S | 缩小跟踪时间轴。 |
D | 在跟踪时间轴上向右平移。 |
E | 以当前鼠标位置为中定位跟踪时间轴。 |
M | 高亮当前选区。 |
1 | 将当前正在使用中的选择模型更改为“选择”模式。对应于鼠标选择器工具栏中显示的第 1 个按钮 |
2 | 将当前正在使用中的选择模型更改为“平移”模式。对应于鼠标选择器工具栏中显示的第 2 个按钮 |
3 | 将当前正在使用中的选择模型更改为“缩放”模式。对应于鼠标选择器工具栏中显示的第 3 个按钮 |
4 | 将当前正在使用中的选择模型更改为“计时”模式。对应于鼠标选择器工具栏中显示的第 4 个按钮 |
G | 在当前所选任务的开头显示网格。 |
Shift + G | 在当前所选任务的末尾显示网格。 |
向左箭头 | 在当前选定的时间轴上选择上一个事件。 |
向右箭头 | 在当前选定的时间轴上选择下一个事件。 |
Systrace 报告列出了渲染界面帧的每个进程,并指明了沿时间轴渲染的每个帧。在 16.6 毫秒内渲染的必须保持每秒 60 帧稳定帧速率的帧会以绿色圆圈表示。渲染时间超过 16.6 毫秒的帧会以黄色或红色帧圆圈表示。 | |
App的主要参数(看包名) | |
点击某个帧圆圈可将其突出显示,并提供有关系统为渲染该帧所做工作的其他信息,包括提醒。此报告还会显示系统在渲染该帧时执行的方法。您可以调查这些方法以确定界面卡顿的可能原因。 | |
选择运行速度慢的帧后,您可能会在报告的底部窗格中看到一条提醒,用于指明问题所在。 | |
选择运行速度慢的帧后,您可能会在报告的底部窗格中看到一条提醒。上图中显示的提醒指明帧的主要问题是在 ListView 回收和重新绑定上花费了太多时间。指向跟踪记录中相关事件的链接可详细说明系统在此期间执行的操作。 |
如需查看此工具在您的跟踪记录中发现的每条提醒以及设备触发每条提醒的次数,请点击窗口最右侧的 Alerts 标签页,下图所示。Alerts 面板可帮助您了解跟踪记录中出现的问题以及这些问题导致出现卡顿的频率。您可以将此面板视为要修正的错误列表。通常情况下,只需对一个区域进行细微改动或改进即可移除整组提醒。
界面性能
使用 GPU 渲染模式分析工具进行分析
界面性能可以查看每一帧的绘制
GPU 渲染模式分析图表中显示的每个竖条中的每个分段都表示流水线的一个阶段,并在条形图中使用特定的颜色突出显示。说明了显示的每种颜色所代表的含义。
了解每种颜色所代表的含义后,就可以针对应用的特定方面来优化其渲染性能。
阶段及其含义
本部分说明与图 2 中的颜色对应的每个阶段发生的操作,以及需要注意的瓶颈原因。
输入处理(input)
流水线的输入处理阶段测量的是应用处理输入事件所用的时间。该指标表示应用执行输入事件回调给出的代码所用的时间。
当此分段较长时
此区域中的值较高通常是由于输入处理程序事件回调中的工作过多或过复杂导致的。 由于这些回调总是发生在主线程上,因此该问题的解决方法主要是直接优化工作,或者将工作转移到其他线程。
另外值得注意的是,此阶段可能出现 RecyclerView 滚动。当 RecyclerView 处理轻触事件时,它会立即滚动。因此,它可能会膨胀或填充新的项目视图。由于这个原因,尽可能加快此操作的执行速度就非常重要。TraceView 或 Systrace 等分析工具可以帮助您进一步调查相关问题。
动画(Anim.)
动画阶段显示的是评估在该帧中运行的所有 Animator 所用的时间。最常见的 Animator 有 ObjectAnimator、ViewPropertyAnimator 和转换。
当此分段较长时
造成此区域中的值较高的原因,通常是因动画的某种属性更改而执行的工作。例如,投掷动画会滚动 ListView 或 RecyclerView,从而导致大量的视图膨胀和填充。
测量/布局(Measure)
为了在屏幕上绘制视图项,Android 会在视图层次结构的布局和视图中执行两项特定操作。
首先,系统会测量视图项。每个视图和布局都包含描述对象在屏幕上的尺寸的具体数据。有些视图具有确切的尺寸,而有些视图的尺寸则可以根据父级布局容器的尺寸进行调整。
然后,系统会对视图项进行布局。系统计算出子视图的尺寸后,就可以进行布局,调整视图在屏幕上的尺寸和位置。
系统不仅会对要绘制的视图进行测量和布局,还会逐层向上对这些视图的父级层次结构执行测量和布局,一直到根视图。
当此分段较长时
如果您的应用在这方面对每帧花费的时间较多,通常是由于需要布局的视图数量过多,或者出现了其他问题(例如在层次结构的错误位置发生了 Double Taxation 等)。在这两种情况下,要解决性能问题,都需要改善视图层次结构的性能。
此外,添加到 onLayout(boolean, int, int, int, int) 或 onMeasure(int, int) 的代码也可能会导致性能问题。Traceview 和 Systrace 可以帮助您检查调用堆栈,以找出代码可能存在的问题。
绘制(Draw)
绘制阶段将视图的渲染操作(如绘制背景或文本)转换为一系列原生绘制命令。系统会将这些命令捕获到一个显示列表中。
“绘制”条记录了将这些命令捕获到显示列表中所用的时间,包括这一帧中需要在屏幕上更新的所有视图对应的命令。测量的时间涵盖了您添加到应用中的界面对象的所有代码。此类代码的示例包括 onDraw()、dispatchDraw() 以及属于 Drawable 类的子类的各种 draw ()methods。
当此分段较长时
简单地说,您可以将该指标理解为针对每个失效视图运行所有 onDraw() 调用所用的时间。该测量值涵盖了将绘制命令分配给可能存在的子项和可绘制对象所用的任何时间。因此,当您看到此竖条出现峰值时,可能是因为大量视图突然失效了。失效意味着必须重新生成视图的显示列表。或者,一些自定义视图的 onDraw() 方法中存在某种极其复杂的逻辑,也可能会导致此时间很长。
同步/上传(Upload)
“同步和上传”指标表示在当前帧中将位图对象从 CPU 内存传输到 GPU 内存所需的时间。
作为两种不同的处理器,CPU 和 GPU 将不同的 RAM 区域用于执行处理。当您在 Android 中绘制位图时,系统会先将位图传输到 GPU 内存,然后 GPU 才能将其渲染到屏幕上。之后,GPU 会缓存位图,以便系统无需再次传输数据,除非纹理被移出 GPU 纹理缓存。
注意:在 Lollipop 设备上,此阶段为紫色。
当此分段较长时
帧的所有资源都必须位于 GPU 内存中才能用来绘制帧。这意味着,该指标的值较高可能表示需要加载大量小型资源或少量大型资源。一种常见的情况是应用要显示一个接近屏幕尺寸的位图。另一种情况是应用要显示大量缩略图。
要减小该值,您可以采用以下技巧:
确保位图的分辨率不会比位图的显示尺寸大很多。例如,您的应用应避免将 1024x1024 的图片显示为 48x48 的图片。
利用 prepareToDraw() 在下一个同步阶段之前异步预上传位图。
发出命令(Issue)
“发出命令”分段表示的是发出将显示列表绘制到屏幕上所需的全部命令所需的时间。
为了将显示列表绘制到屏幕上,系统会向 GPU 发送必要的命令。通常,系统会通过 OpenGL ES API 执行此操作。
此过程需要一些时间,因为在将命令发送给 GPU 之前,系统会对每个命令执行最后的转换和裁剪。然后 GPU 会计算最终的命令,使得 GPU 端的开销增加。这些命令包括最后的转换和额外的裁剪。
当此分段较长时
在此阶段花费的时间直接反映了系统在给定帧中渲染的显示列表的复杂程度和数量。例如,如果有大量绘制操作,特别是在每个绘制基元都有较少的固有开销的情况下,将会使得这项时间增加。 例如:
Kotlin
for (i in 0 until 1000) {
canvas.drawPoint()
}
Java
for (int i = 0; i < 1000; i++) {
canvas.drawPoint()
}
发出以上命令的开销会远远大于发出以下命令的开销:
Kotlin
canvas.drawPoints(thousandPointArray)
Java
canvas.drawPoints(thousandPointArray);
发出命令与实际绘制显示列表之间并不总是存在 1 对 1 的关系。“发出命令”阶段捕获的是将绘制命令发送到 GPU 所用的时间,而“绘制”指标表示的是将已发出的命令捕获到显示列表所用的时间。
之所以会出现这种差异,是因为系统会尽可能地缓存显示列表。因此,在某些情况下,滚动、转换或动画会要求系统重新发送显示列表,但不必实际重新构建它(即重新捕获绘制命令)。因此,您可能会看到“发出命令”条较高,但“绘制命令”条并不高。
处理/交换缓冲区(Swap)
当 Android 将其所有显示列表提交给 GPU 后,系统会发出最后一条命令,告诉图形驱动程序它已完成当前帧的处理。此时,驱动程序即可将更新后的图像显示到屏幕上。
当此分段较长时
有一点必须要注意:GPU 与 CPU 是并行工作的。Android 系统向 GPU 发出绘制命令,然后继续执行下一个任务。GPU 从队列中读取并处理这些绘制命令。
如果 CPU 发出命令的速度快于 GPU 处理命令的速度,这两个处理器之间的通信队列就会被占满。出现这种情况时,CPU 会阻塞并等待,直到队列中有位置来放置下一个命令。这种队列占满状态通常出现在“交换缓冲区”阶段,因为此时已提交了整个帧的命令。
缓解此问题的关键是降低 GPU 工作的复杂度,就像您在“发出命令”阶段所做的那样。
其他(Misc)
除了渲染系统执行其工作所用的时间外,主线程上还会执行一些与渲染无关的工作。这些工作耗费的时间被报告为“其他时间”。“其他时间”通常表示可能在两个连续渲染帧之间的界面线程上执行的工作。
当此分段较长时
如果该值很高,则表示您的应用可能包含应在其他线程上执行的回调、Intent 或其他工作。通过方法跟踪或 Systrace 等工具可以查看主线程上运行的任务。此信息可帮助您有针对性地改进性能。
遇到问题1
D:\Python27>python.exe D:\Android-SDK\platform-tools\systrace\systrace.py
Traceback (most recent call last):
File "D:\Android-SDK\platform-tools\systrace\systrace.py", line 48, in <module>
from systrace import run_systrace
File "D:\Android-SDK\platform-tools\systrace\catapult\systrace\systrace\run_systrace.py", line 41, in <module>
from devil import devil_env
File "D:\Android-SDK\platform-tools\systrace\catapult\systrace\systrace\..\..\devil\devil\devil_env.py", line 33, in <module>
import dependency_manager # pylint: disable=import-error
File "D:\Android-SDK\platform-tools\systrace\catapult\dependency_manager\dependency_manager\__init__.py", line 29, in <module>
from .archive_info import ArchiveInfo
File "D:\Android-SDK\platform-tools\systrace\catapult\dependency_manager\dependency_manager\archive_info.py", line 9, in <module>
from dependency_manager import exceptions
File "D:\Android-SDK\platform-tools\systrace\catapult\dependency_manager\dependency_manager\exceptions.py", line 5, in <module>
from py_utils import cloud_storage
File "D:\Android-SDK\platform-tools\systrace\catapult\common\py_utils\py_utils\cloud_storage.py", line 22, in <module>
from py_utils import lock
File "D:\Android-SDK\platform-tools\systrace\catapult\common\py_utils\py_utils\lock.py", line 20, in <module>
import win32con
ImportError: No module named win32con
解决办法:
D:\Python27\Scripts>pip install pypiwin32
DEPRECATION: Python 2.7 will reach the end of its life on January 1st, 2020. Please upgrade your Python as Python 2.7 won't be maintained after that date. A future version of pip will drop support for Python 2.7. More details about Python 2 support in pip, can be found at https://pip.pypa.io/en/latest/development/release-process/#python-2-support
Collecting pypiwin32
Downloading https://files.pythonhosted.org/packages/13/e8/4f38eb30c4dae36634a53c5b2cd73b517ea3607e10d00f61f2494449cec0/pypiwin32-223.tar.gz
Collecting pywin32>=223 (from pypiwin32)
Downloading https://files.pythonhosted.org/packages/20/41/e266bb854b3d352641a4eaafe5b6b7ea3944039a9a1014ec481418e7f982/pywin32-228-cp27-cp27m-win_amd64.whl (7.5MB)
|████████████████████████████████| 7.5MB 29kB/s
Installing collected packages: pywin32, pypiwin32
Running setup.py install for pypiwin32 ... done
Successfully installed pypiwin32-223 pywin32-228
WARNING: You are using pip version 19.2.3, however version 20.3.4 is available.
You should consider upgrading via the 'python -m pip install --upgrade pip' command.
遇到问题2
D:\Python27>python.exe D:\Android-SDK\platform-tools\systrace\systrace.py
Traceback (most recent call last):
File "D:\Android-SDK\platform-tools\systrace\systrace.py", line 48, in <module>
from systrace import run_systrace
File "D:\Android-SDK\platform-tools\systrace\catapult\systrace\systrace\run_systrace.py", line 41, in <module>
from devil import devil_env
File "D:\Android-SDK\platform-tools\systrace\catapult\systrace\systrace\..\..\devil\devil\devil_env.py", line 33, in <module>
import dependency_manager # pylint: disable=import-error
File "D:\Android-SDK\platform-tools\systrace\catapult\dependency_manager\dependency_manager\__init__.py", line 29, in <module>
from .archive_info import ArchiveInfo
File "D:\Android-SDK\platform-tools\systrace\catapult\dependency_manager\dependency_manager\archive_info.py", line 10, in <module>
from dependency_manager import dependency_manager_util
File "D:\Android-SDK\platform-tools\systrace\catapult\dependency_manager\dependency_manager\dependency_manager_util.py", line 12, in <module>
import six
ImportError: No module named six
解决办法:
D:\Python27\Scripts>pip install six
DEPRECATION: Python 2.7 will reach the end of its life on January 1st, 2020. Please upgrade your Python as Python 2.7 won't be maintained after that date. A future version of pip will drop support for Python 2.7. More details about Python 2 support in pip, can be found at https://pip.pypa.io/en/latest/development/release-process/#python-2-support
Collecting six
Downloading https://files.pythonhosted.org/packages/d9/5a/e7c31adbe875f2abbb91bd84cf2dc52d792b5a01506781dbcf25c91daf11/six-1.16.0-py2.py3-none-any.whl
Installing collected packages: six
Successfully installed six-1.16.0
WARNING: You are using pip version 19.2.3, however version 20.3.4 is available.
You should consider upgrading via the 'python -m pip install --upgrade pip' command.