本篇介绍几个Android上用于进行性能分析的工具,都是SDK自带的,每个都够强大够大家细细分析使用。
先来说说TraceView
这个工具SDK文档中说的比较随意,就大概说了一下有什么。但是真正要使用它来分析问题还需要详细的了解一下怎么用并且快速的定位到问题所在。
下面将分四部分来讲:
首先是基本操作:
1、获取trace信息,这个有两种办法,一是在DDMS界面选择需要检查的进程,然后再点击Start Method Profiling,会出来一个模式选择框,大家就选择默认的Sample based profiling就好了,Trace base那个由于追踪了每个方法会导致界面卡顿会影响测试效果。结束点击同一个按钮Stop即可。
2、这个在SDK的文档上说的比较多,在需要测试的代码中添加下面两个方法,把trace写入到sdcard上的文件中,然后可以拷贝到电脑上用命名行来启动Traceview查看这个文件,得到和第一种方式一样的视图。
// start tracing to "/sdcard/calc.trace" Debug.startMethodTracing("calc"); // ... // stop tracing Debug.stopMethodTracing();
注:trace文件还可以借助dmtracedump
生成另外一种堆栈调用的树形视图,这个后面讲。
然后是视图结构:
前面两个操作得到的视图入上图所示,分为三个区域,1区域中显示的是各个线程、2区域是时间轴,这里显示的是各个方法执行的时间上的关系、3区域是方法详情,展示的是各个方法的调用情况以及一些参数信息。选择时间区域内会执行放大操作将所选择区域放到显示,双击右上角或者时间轴可以换成原来大小,但是这个基本不会使用因为即使放大了也很难看清楚每个方法,一般都是直接看区域3。
区域3用来分析每个方法的CPU和实际耗时,那么就必须对这些指数逐一认识。
Name:这个很明显就是各个方法名,展开后包括Parents和Children,Parents是调用者,Children是此方法中调用的其他方法以及自身,表明的就是方法堆栈调用信息。
Incl Cpu Time%和Incl Cpu Time:这两个都是说的这个方法体整体占用CPU的时间和比例,这个比例的分母是谁呢?来看下面这个
这个0(toplevel)就是代表的这个监测过程的整个trace,所有的百分比都是根据它计算出来的,后面就只需要解释不带%的数值就行了。
继续来看,展开之后可以看到Parents和Children各个方法的时间和占比,对于Parents来说就是当前方法在这个父方法中所占的时间和比例,Children各个子项就是子方法在本方法总的执行时间中所占数值和比例。
Excl Cpu Time%和Excl Cpu Time:除外的CPU时间,这个怎么说呢,这个还是猜测,有些情况下这个数值如区域3的图中两个红色的区域,数值是一样的,也就是说这个时间是方法本身除调用子方法所花费的时间,但是有时候这个数值时没有关系的,self为零Excl Cpu Time也是有值的,我猜测应该是进程调度,方法调用等其他系统处理所耗费的CPU时间。如果这个值过大比例很高,那么耗时问题主要就在本方法体内了,子方法耗时相对较少。
对应的上面的参数都有一组叫Real的参数,这个所指的是实际的运行时间非CPU时间(包括CPU时间,切换时间和等待时间,但是Looper.loop, NativeStart.main, Method.invoke等几个方法的时间比较诡异,本人还没弄明白为什么!),与区域1时间轴上的时间区间和长度是一致的。
Calls+RecurCalls/Total:这是表示该方法调用的次数和递归调用的次数。
Cpu Time/Call:该方法调用一次占用的Cpu时间。
Real Time/Call:该方法执行一次实际所花时间。
快速定位问题:
通过上述对各个参数的解释,相信大家心里都有一个直观的意识,那就是只需要关注Real Time的有关参数并且Real Time/Call值较高或者Calls+RecurCalls/Total较高的都是需要重点关注的对象,因为单个方法执行耗时大部分情况需要优化的,而多次调用需要看看流程中是否可以减少调用次数。
一般来说Android上一个功能的实现出现了性能问题,可以将出现问题的操作功能这样分析一下,直接将Incl Real Time按大小排列一下,挑几个大头来仔细看看(最开始的几个比如Looper.loop, NativeStart.main, Method.invoke的可以忽略,这几个耗时也是后面的操作造成的,也不要漫无目的的找要针对自己分析的问题来,本文举例是启动速度的有很多系统方法的干扰,查滑动卡就简单多了。)看看调用次数较多的是否与设计逻辑不一致或者可以减少,看看执行时间较长的方法是不是有执行数据库操作啊、存储读写啊、创建Bitmap啊,或者解析很复杂的布局等等。
堆栈树形视图结构:
这是需要用到SDK中的dmtracedump工具,怎奈我自己试了很多次都无法读取生成的trace文件,没办法在这说经验了,给大家推荐一个帖子吧:http://blog.csdn.net/yiyaaixuexi/article/details/6716884
本人实际使用和写这篇博客有参考此篇http://www.oschina.net/news/56500/traceview-android
然后是systrace
这个是分析系统绘制性能的工具,比如动画为什么掉帧啦不流畅等问题。systrace它把应用和整个系统的运行情况放在同一个时间线上分析更加直观。
首先说一下基本操作,在DDMS界面左边的Devices视窗中选择Capture system wide trace using Android systrace这个,会弹出一个Android System Trace的选择框,里面需要配置此次收集的参数:trace文件存储的路径,时长以及缓存的大小,最后是最重要的就是我们需要关注tags,一般来说需要根据我们分析的场景设想一下可能的原因来选择(比如我们分析动画掉帧的情况就可以选上Graphics、ResourceLoading、Dalvik VM和Synchronization标签),设置之后ok就进行收集状态,这时做我们需要分析的操作得到一个trace.html文件,在Chrome浏览器中输入chrome://tracing/,load生成的tram.html文件即可。
先来看一个列表滑动正常的测试代码得到的systrace图:
可以简要的看一下这个图中包含了一些什么内容,这张图中,看看VSYNC和surfaceflinger的绘制间隔都保持在16.6ms以内并且很好的对应了起来,这样的绘制时能满足60帧的。但是如果如下图所示就很不乐观了,下面我故意在Adapter的getView方法中放置了创建Bitmap的操作使得listview滑动起来卡顿,得到了如下的systrace图:
看上面这个简单的例子,很容易看出来由于decodeBitmap和GC,使得绘制间隔已经不是原来的16.7ms了,目前是34.33ms,也就是说现在这个只有30帧左右了,理所当然的看上去就会有卡顿的效果了,改也很好改,将decodeBitmap过程放到子线程中并且做缓存处理减少GC。
接下来提一下Allocation Tracker
这个东西顾名思义啊,就是跟踪内存分配的,在DDMS中选择一个进程然后在Allocation Tracker窗口页面中点击Start Tracking,就进入了跟踪状态,执行一个操作之后,点击get Allocatios就会在下面列出当前内存分配的情况如下图所示:
这里展示了当前分配内存分配的序号、大小、类型、线程号、内容对象所在的类和方法。比如我在主线程中创建了一张bitmap,就会看到这样的记录:
这样就很容易跟踪当前操作区域是否有不必要的内存情况,这个工具列出的是应用运行时整个系统分配内存的情况包括framework部分的执行,特别适合分析当前应用程序运行内存峰值很高的情况,可以看到都是哪些对象占用了较多内存能不能优化;当然不能分析内存泄露啊,那个得靠Heap和MAT分析了。
其实另外还有一个NB的工具OpenGL Trace,这个东西一般用来分析OpenGL的绘制本人还没用过就不说了。
PS:写的差不多的时候,无意间网上看了一篇大神的文档:http://blog.csdn.net/innost/article/details/9008691,这个汗颜啊,说一样的东西,那文笔和思路巨清晰,确实对初次使用的人有很大帮助。我还得加油啊。