2.2 性能分析工具
从前一节可以看到,Android系统在4.1以后从框架上解决了由于系统问题导致的卡顿现象,但在实际的使用过程中,在用户的感受上,卡顿仍然是应用开发中主要面临的问题,而原因从上一节的分析中也知道本质是VSync信号到来时,不能及时处理绘制事件导致,本节先抛出以下两个问题:
1)应用层做了什么会导致VSync事件不能及时处理?
2)卡顿能监控吗?
性能问题并不容易复现,也不好定位,光从几个场景不能完全覆盖所有的问题,因此在做性能优化时,最直接有效的方法,就是尽量复现存在性能问题的场景,并监控此过程中程序的执行流程,如果能够方便地分析程序中函数的调用关系和执行时间,自然也就很容易找出性能瓶颈。
分析问题和确认问题是否解决,都借助了相应的调试工具,比如查看Layout层次的Hierarchy View、Android系统上带的GPU Prof?ile工具和静态代码检查工具Lint等。这些工具对性能优化都起到非常重要的作用。本节将介绍这些工具和另外两个性能优化非常重要的工具:TraceView和Systrace。这两个工具除了在UI上,对于在后面将要讲到的启动优化、动画优化等上都是很重要的工具,可以说大部分的性能分析都离不开这几个工具,接下来学习几个常用的与流畅度优化相关的工具的使用方法,在后面实际的优化方案中也会介绍其他辅助工具。
2.2.1 卡顿检测工具
要做性能优化,就非常有必要借助于一系列辅助工具,Android提供了多个开发辅助工具,在性能调优过程中非常重要,下面介绍几个常用的工具,在后面的具体优化过程中会多次使用到。
我们已经知道,从应用层绘制一个页面(View),主要有三个过程:CPU准备数据→GPU从数据缓存列表获取数据→Display设备绘制,这三个过程的耗时可以通过一个手机开发辅助工具查看:Prof?ile GPU Rendering。Prof?ile GPU Rendering是Android 4.1系统开始提供的一个开发辅助功能,在设置中打开开发者选项,如图2-12所示。
Prof?ile GPU Rendering功能特点如下:
它是一个图形监测工具,能实时反应当前绘制的耗时。
横轴表示时间,纵轴表示每一帧的耗时(单位为ms)。
随着时间推移,从左到右的刷新呈现。
提供了一个标准的耗时,如果高于标准耗时,表示当前这一帧丢失。
如果设置中没有开发者选项,可以通过设置页面中的“关于”选项,单击版本号七次即可打开开发者选项,在后面的章节中还会使用到开发者选项中的其他辅助工具。
打开Prof?ile GPU Rendering后可以看到实时刷新的彩色图,如图2-13所示。每一根竖线表示一帧,由多个颜色组成,不同颜色的解释如下:
图2-12 打开Prof?ile GPU rendering 图2-13 Prof?ile GPU rendering
每一条柱状图都由4种颜色组成:红、黄、蓝、紫,这些线对应每一帧在不同阶段的实际耗时。
蓝色代表测量绘制的时间,它代表需要多长时间去创建和更新DisplayList。在Android中,一个视图在进行渲染之前,它必须被转换成GPU熟悉的格式,简单来说就是几条绘图命令,蓝色就是记录了在屏幕上更新视图需要花费的时间,也可以理解为执行每一个View的onDraw方法,创建或者更新每一个View的Display List对象。在蓝色的线很高时,有可能是因为需要重新绘制,或者自定义视图的onDraw函数处理事情太多。
红色代表执行的时间,这部分是Android进行2D渲染Display List的时间,为了绘制到屏幕上,Android需要使用OpenGl ES的API接口来绘制Display List,这些API有效地将数据发送到GPU,最终在屏幕上显示出来。当红色的线非常高时,可能是由重新提交了视图而导致的。
橙色部分表示处理时间,或者是CPU告诉GPU渲染一帧的地方,这是一个阻塞调用,因为CPU会一直等待GPU发出接到命令的回复,如果柱状图很高,就意味着GPU太繁忙了。
紫色段表示将资源转移到渲染线程的时间,只有Android 4.0及以上版本才会提供。
在实际开发中,从图上虽然可以看到绘制的时间,但对不便于进行数据分析,比如进入某一个页面,柱形图虽然实时绘制出来,但不能更好地分析,这里可以通过:adb shell dumpsys gfxinfo com.**.**(包名)把具体的耗时输出到日志中来分析。
任何时候超过绿线(警戒线,对应时长16ms),就有可能丢失一帧的内容,虽然对于大部分应用来说,丢失几帧确实感觉不出卡顿,但保持UI流畅的关键就在于让这些垂直的柱状条尽可能地保持在绿线下面。
GPU Prof?ile工具能够很好地帮助你找到渲染相关的问题,但是要修复这些问题就不是那么简单了。需要结合另一个耗时工具和代码来具体分析,找到性能的瓶颈,并进行优化。在GPU Prof?ile Render发现有问题的页面后,可以通过另外一个工具Hierarchy Viewer来查看页面的布局层次和每个View所花的时间,在后面的布局优化章节,将通过实例来讲解和学习使用方法。
2.2.2 TraceView
TraceView是AndroidSDK自带的工具,用来分析函数调用过程,可以对Android的应用程序以及Framework层的代码进行性能分析。它是一个图形化的工具,最终会产生一个图表,用于对性能分析进行说明,可以分析到应用具体每一个方法的执行时间,使用可以非常直观简单,分析性能问题很方便。
1.?使用方法
在使用TraceVeiw分析问题之前需要得到一个*.trace的文件,然后通过TraceView来分析trace文件的信息,trace文件的获取有两种方式:
(1)在DDMS中使用
1)连接设备。
2)打开应用。
3)打开DDMS(若在Android Studio中则先打开Android Device Monitor)。
4)单击Strart Method Prof?iling按钮,如图2-14所示。
图2-14 在DDMS中打开TraceView
5)在应用中操作需要监控的点,比如进入一个Activity或者滑动一个列表,完成后单击Stop Method Prof?iling按钮,如图2-15所示。
图2-15 结束一次TraceView
6)结束会自动跳转到TraceView视图。
这种方法使用方便,但监控范围不够精确,如果需要精确监控某一个路径,就需要使用下一个方法:在代码中加入调试语句保存Trace文件。
(2)代码中加入调试语句保存trace文件
有时在开发过程中不好复现的问题,需要在关键的路径上获取TraceView数据,在测试时复现此问题后直接拿到Trace文件查看对应的数据。这时可以在代码中使用TraceView工具并生成对应的trace文件。在android.os.Debug类中提供了相应的方法,过程很简单步骤如下:
1)在需要开始监控的地方调用startMethodTracing()。
2)在需要结束监控的地方调用stopMethodTracing()。
3)系统会在SD卡中创建<trace-name>.trace文件。
4)使用traceveiw打开该文件进行分析。
调用代码如下:
// start tracing to "/sdcard/ui_performance.trace"
Debug.startMethodTracing("ui_performance");
// ...
// stop tracing
Debug.stopMethodTracing();
在应用程序中调用startMethodTracing()时,系统会在指定的路径上创建一个名为<trace_f?ilename>.trace文件。这个文件包含了方法名跟踪数据,以及与线程和方法名的映射表。然后系统开始缓存应用产生的跟踪数据,直到应用程序调用stopMethodTracing()结束,此时将其缓冲的数据写入输出文件中。如果系统达到最大缓存大小时,还没有调用stopMethodTracing(),系统会停止跟踪并发送一个通知。
在Android 4.4及更高版本中,可以通过基于采样的方法分析耗时情况,因为减少了分析Trace文件的次数,降低了IO读写,所以可以减少Trace工具在运行时对性能的影响,同时对分析结果也不会有很大的偏差。通过调用startMethodTracing()方法,就可以指定具体的采样间隔,定期采集样本数据分析。
在代码中使用此方法保存TraceView数据,不要忘记在应用中打开write to external storage权限(WRITE_EXTERNAL_STORAGE)。
2.?TraceView视图说明
Traceview视图分两部分,上半部分为时间片面板(Timeline Panel),下半部分为分析面板(Prof?ile Panel)。
时间片面板如图2-16所示。
图2-16 时间片面板
X轴表示时间消耗,单位为毫秒(ms),Y轴表示各个线程,每个线程中的不同方法使用了不同的颜色来表示,颜色占用面积越宽,表示该方法占用CPU时间越长。
时间片面板可以放大/缩小,也可以指定区域放到最大,方便查看具体的过程,一般优先选择放大耗时严重的区域。
分析面板(Prof?ile Panel)如图2-17所示。
图2-17 分析面板
分析面板看起来并不复杂,但需要理解各列数据的意义,每一列表示的意义如表2-1所示。
表2-1 分析面板参数意义
列 名 意 义
Name 所有的调用项,展开可以看到有的有Parent和Children子项,指被调用和调用
Inclusive 统计函数本身运行的时间+调用子函数运行的时间
incl inclusive时间占总时间的白分比
Exclusive 同级函数本身运行的时间
Excl 执行占总时间的白分比
Calls + Recur Calls / Total 该方法调用次数+递归次数
Cpu Time / Call 该方法耗时
Real Time / Call 实际时长
使用TraceView查看耗时,主要关注Calls + Recur Calls / Total和Cpu Time / Call这两个值,也就是关注调用次数多和耗时久的方法,然后优化这些方法的逻辑和调用次数,减少
耗时。
RealTime与cputime区别为:因为RealTime包括了CPU的上下文切换、阻塞、GC等,所以RealTime方法的实际执行时间要比CPU Time稍微长一点。
2.2.3 Systrace UI性能分析
在应用程序开发过程中,UI(用户界面)的流畅度是体验的核心,特别是在动画、跳转或者列表的滑动过程中,出现卡顿和无响应是非常影响用户体验的,要解决这些问题,首先要找到问题的原因,前面介绍的TraceView是分析性能的一款利器,下面再介绍一个分析应用程序UI性能的工具:Systrace。
Systrace是Android 4.1及以上版本提供的性能数据采样和分析工具。它可以帮助开发者收集Android关键子系统(如surfacef?linger、WindowManagerService等Framework部分关键模块、服务,View系统等)的运行信息,从而帮助开发者更直观地分析系统瓶颈,改进性能。Systrace的功能包括跟踪系统的I/O操作、内核工作队列、CPU负载等,在UI显示性能分析上提供很好的数据,特别是在动画播放不流畅、渲染卡等问题上。Systrace工具可以跟踪、收集、检查定时信息,可以很直观地查看CPU周期消耗的具体时间,显示每个线程和进程的跟踪信息,使用不同颜色来突出问题的严重性,并提供如何解决这些问题的建议。
由于Systrace是以系统的角度返回一些信息,并不能定位到具体耗时的方法,要进一步获取CPU满负荷运行的原因,就需要使用前面介绍过的工具Traceview。
1.?Systrace使用方法
Systrace的使用不复杂。但跟踪的设备必须是Android 4.1(API16)或更高版本。在4.3版本和4.3以前版本的使用上有些区别,后面会讲到。
4.3以前系统版本的设备需要打开Settings > Developer options > Monitoring > Enable traces。
(1)在DDMS上使用
在Eclipse和Android Studio中都可以在DDMS直接使用Systrace,其他IDE也能支持,且流程都相同,下面以Android Studio为例说明其使用流程。
1)打开Android Device Monitor,连接手机并准备需要抓取的界面。
2)单击Systrace按钮进入抓取前的设置,选择需要跟踪的内容(见图2-18):
3)手机上开始操作需要跟踪的过程(如滑动列表)。
4)到了设定好的时间后,生成Trace文件。
5)使用Chrome打开文件即可分析。
(2)使用命令行
使用命令行方式更灵活,速度更快,并且配置好后再使用能快速得到结果,在Android 4.3及更高版本的设备上使用Systrace时,可以省略设置跟踪类别标签来获取默认值,或者可以手动列入指定标签。命令如下:
$ cd android-sdk/platform-tools/systrace
$ python systrace.py --time=10 -o mynewtrace.html sched gfx view wm
其中参数设置对应的功能如表2-2所示。
表2-2 System参数命令
参数名 意 义
-h, --help 帮助信息
-o <FILE> 保存的文件名
-t N, --time=N 多少秒内的数据,默认为5秒,以当前时间点往后倒N秒时间
-b N, --buf-size=N 单位为千字节,限制数据大小
-k <KFUNCS> --ktrace=<KFUNCS> 追踪特殊的方法
-l, --list-categories 设置需要追踪的标签
-a <APP_NAME>, --app=<APP_NAME> 包名
--from-f?ile=<FROM_FILE> 创建报告的来源trace文件
-e <DEVICE_SERIAL>, --serial=<DEVICE_SERIAL> 设备号
其中categories中的标签比较多,可以从官方的文档上查询:http://developer.android.com/intl/zh-cn/tools/help/systrace.html
(3)应用中获取
Systrace不会追踪应用的所有工作,所以在有需求的情况下,需要添加要追踪的代码部分。在Android 4.3及以上版本的代码中,可以通过Trace类来实现这个功能。它能够让你在任何时候跟踪应用的一举一动。在获取Trace的过程中,即Trace.beginSection()与Trace.endSection()之间的代码工作会一直被追踪。
在代码中加入Trace跟踪需要注意以下两点:
在Trace被嵌套在另一个Trace中时,endSection()方法只会结束离它最近的一个beginSection(String),即在一个Trace的过程中是无法中断其他Trace的。所以要保证endSection()与beginSection(String)调用次数匹配。
Trace的begin与end必须在同一线程中执行。
下面这部分代码为使用Trace的例子,在整个方法中含有两个Trace块,可以根据需求定义更多的块,但都要成对出现,如果有开始块但没有结束块,会严重影响应用的性能。
public void ProcessPeople() {
Trace.beginSection("ProcessPeople");
try {
Trace.beginSection("Processing Jane");
try {
// code for Jane task...
} finally {
Trace.endSection(); // ends "Processing Jane"
}
Trace.beginSection("Processing John");
try {
// code for John task...
} finally {
Trace.endSection(); // ends "Processing John"
}
} finally {
Trace.endSection(); // ends "ProcessPeople"
}
}
2.?分析Systrace报告
通过前面方法获取到的trace.html文件,需要使用Chrome打开,有一些常用的快捷键,定义如表2-3所示。
目前Systrace产生的trace文件只能使用Chrome打开,使用Chrome打开文件后如图2-19所示。
从图2-19中可以看到完整的数据,其中和UI绘制关系最密切的是Alerts和Frame两个数据,接下来重点介绍Alerts和Frame。
图2-19 Systrace Viewer
(1)Alerts
从图2-19可以看到,Alerts一栏标记了性能有问题的点,单击该点可以查看详细信息,在右边侧边栏还有一个Alerts框,单击可以查看每个类型的Alerts的数量,单击某一个Alert可以看到问题的详细描述。
(2)Frame
每个应用都有一行专门显示frame,每一帧就显示为一个绿色的圆圈。当显示为黄色或者红色时,它的渲染时间超过了16.6ms(即达不到60fps的水准)。使用W键放大,看看这一帧的渲染过程中系统到底做了什么,同时它会将任何它认为性能有问题的东西都高亮警告,并提示要怎么优化。如图2-19所示,在Frame栏有一个F帧(第二帧)黄色告警,从下面的问题详细描述可以看出,警告的主要原因是ListView的回收和重新绑定花费太多时间。在Systrace中也会提供一些对应链接,提供更多解释。
如果想知道UI线程怎么会花费这么多时间的话,就需要使用2.2.2节讲到的TraceView,来分析具体是哪些函数在消耗时间。