TraceView是Android SDK中提供的工具,可以看到线程及方法执行时间、调用次数等。
目前Traceview已弃用,官方推荐使用Android profiler中的CPU性能剖析器,但有些老版本(如Android 4.4)并不支持profiler,所以这两个我们都来了解下。
一.TraceView
1.打开android sdk/tools/monitor.bat文件。
2.选中要分析的进程点击红框处的按钮开始即可,再点击一下结束记录。
结果页面如下:
图片2.png很直观,基本就是把应用里所有的线程及方法按不同维度的时间消耗来排列了下。
1处列出了当前应用里的线程
2处是监控的总时长,这个是由手动控制的
3处线程在这个时间段中执行的情况,都调用了哪些方法。这些方法和4处一一对应,点击可跳转。图示里可以明显的看出几乎所有的操作都集中在main线程中进行。
4处显示方法的详情,包括调用此方法的parents,此方法调用的Children,以及各种时间项。
时间项我们主要关注以下两个即可(详细说明在后面) :
Cpu Time/Call : 方法消耗CPU的时间(平均值)
Calls + Recur Calls/Total: 方法调用的次数(Calls表示这个函数的调用次数,而Recur Calls表示递归调用次数)
我们可以分别按这两列排序,看应用中哪些线程最耗时/调用次数最多,从而可以针对性的优化。
如果想查看某个方法怎么办呢?可以在最下方的find搜索栏搜索,避免海底捞针。
二.Android studio profiler
如下图,切换到CPU栏,再选择 Trace java Methods,最后点击Record即可。
(注意:record时间不要过长,很容易卡死,4,5秒就可以了,也要看具体的设备)
结果页面如下:
图片4.png与DDMS类似,1处显示了线程列表,2处显示了此线程下的方法执行调用情况。
有一些知识点需要说下:
1处的Threads有三种颜色,分别是
橙色(黄色):系统 API
绿色:自己的方法
蓝色:第三方 API(包括 Java 语言 API)
2处的线程详情有三个时间相关的列,Total Self Children,它们分别代表着
Total 方法执行的总时间
Self 方法执行的自身时间(排除方法中调用的方法)
Children 方法中所调用的方法执行的时间
2处 还有四个tab页:summary,Top down,Flame Chart,Bottom up,这四个tab页以不同的视角去查看线程中方法的执行情况。每一个线程都会有这四个选项。
Summary:汇总页,这里列出了10个运行时间最长的方法(CPU Duration)
Top down:自上而下的树状结构(一棵树),根节点是调用者。
图片6.png 图片7.pngFlame Chart:火焰图,下大上小,就像火焰一样(不是树状结构了)
图片8.png 图片9.pngBottom Up:自下而上的树状结构(多棵树),根节点是被调用者
图片10.png 图片11.png这几种图,一般看Top down就可以了。
Tips:
1.这里有个非常便利的功能就是在方法上右键 Jump to Source,可以直接跳转到源码。
2.如果想要更细的控制粒度,使用代码也可以记录, 生成的.trace文件用ddms或者as都可以打开。
//开始
Debug.startMethodTracing("TraceviewTest");
//结束
Debug.stopMethodTracing();
(文件保存到 "/sdcard/TraceviewTest.trace", 高版本的可能会保存到/sdcard/Android/data/packageName/files/下)
总结:
通过这两个工具一顿操作之后,应用的CPU运行状况就一目了然,耗时长、调用次数多的线程/方法就是要重点关注的对象。还要避免应用内的线程数量过多,如果超过进程的最大值话会造成OOM(没错,不光是内存会造成OOM,线程也会)。
可以通过adb shell 查看 /proc/sys/kernel/threads-max这个文件,它规定了每个进程创建线程数目的上限。(一般是几万,有的手机也有几百的)。另外尽量避免使用无名线程,因为不好跟踪,原则上都要命名且最好统一使用线程池。
至于怎么去优化,还是老生常谈的那些东西。。。说是CPU优化,实际上是代码的优化、线程的优化、同步的优化,这个还是要去结合实际的业务场景去看,不能一蹴而就。
备注:
1.ddms的各个时间列:
Incl Cpu Time:方法在CPU中执行所有时间(包含其调用的方法所消耗的时间)
Excl Cpu Time: 方法在CPU中执行的时间(不包含其调用的方法所消耗的时间)
Incl Real Time:方法运行消耗的所有时间(包含子方法)
Excl Real Time:方法运行消耗的时间(不包含子方法)
Calls + Recur Calls/Total :方法调用、递归次数占总次数百分比(重要指标,防止死循环)
Cpu Time/Call :该方法平均占用 CPU 的时间(重要指标,可以看出单个方法占用CPU的平均时间,但是要防止在个别调用处出现长时间占用,然后被平均了)
Real Time/Call :平均执行时间,包括切换、阻塞的时间(重要指标,可以看出单个方法执行的平均时间值,但是要防止在个别调用处出现长时间调用,然后被平均了)
2.Thread.getAllStackTraces()可以得到进程中的所有线程以及对应的堆栈信息。
网友评论