Android性能优化相关

Speed Up Your App(1)

2016-03-26  本文已影响136人  e95349844edd

翻译自Speed Up Your App

几周之前,我在Droidcon NYC分享了一次关于Android 性能优化的报告。为了在报告上展示出一个性能优化的真实案例,我花了很长时间来准备,但因为时间限制还是删减了一大半的ppt。这篇文章是对报告中没有提到的部分进行总结。

这是视频的链接(需要翻墙)

现在我们来回顾下几个重要的点,希望可以把整个过程讲清楚。那么先从我在优化时遵循的几个准则讲起:

三个准则

每次遇到和性能相关的问题,我都会遵循以下三个准则:

量化

总是用肉眼来判断性能好坏并不是个好主意。比如说,同一个动画你观察好几次之后,可能会有速度越来越快的错觉。而数据是不会说谎的,利用接下来要介绍的几个工具来看下你的app性能怎么样。

用渣手机

如果真要找到所有的性能短板,那么用一个渣配置的手机可能会好一点。因为配置高的设备可能不容易把性能问题暴露出来,毕竟用户的手机不全是最新的。

权衡

性能优化其实就是权衡。优化了一个地方会可能带来其他方面的代价。很多时候还要花点时间解决性能优化带来的影响,比如bitmap的质量问题或者缓存问题,所以做好牺牲某方面的准备吧。

Systrace

Systrace是最好用的工具之一,可能很多人没用过。主要是开发人员不知道如何处理它提供的数据信息。

Systrace可以展现当前手机的运行状况。这个工具告诉我们,手机其实是一个可以同时干很多事情的计算机。最近更新的SDK tools里,Systrace提供了可视化的界面,帮助我们更有效的找到问题。让我们先来看下这个工具:

Fig. 1

可以用Android Device Monitor 工具或者命令行生成trace file,更多信息请参考这里

视频里我提到了几个部分,其中比较重要的两个是Alerts和Frames。先看下我截的一个trace图片,选择其中最上面的一个alert点

Fig. 2

这个alert显示View#draw()方法耗时太长(10.102ms),同时还有文档描述甚至还有视频链接。再往下看Frames部分,图中画出了每一帧的渲染情况,绿色,黄色和红色分别对应显示不同的性能。选择其中一段红色部分:

Fig. 3

在最底下可以看到,和这一帧相关的所有alert。上面显示有3个alert,其中一个我们上面提到过。我们放大一下,可以找到“Inflation during ListView recycling”这个alert,如下图:

Fig. 4

可以看到这部分耗时32ms,导致这一帧的渲染时间超出了60fps所要求的16ms界限。还给出了所在帧的ListView里每个item的耗时细节,大概6ms一个item,我们一共有5个items。描述信息部分能帮我们理解问题,有时候还能提供解决方案。通过上面的图中可以看到所有的东西,并且可以放大“inflate”部分,找到layout中具体在哪个view上比较耗时。

另外一个渲染卡顿的例子:

Fig. 5

选定一帧之后,按下M键可以高亮并且能展示这一帧的渲染情况。上图显示这帧耗时19ms,展开发现唯一一个alert,提示出现“Scheduling delay”。

“Scheduling delay”意思是处理某一slice的线程很长时间得不到cpu的调度,因此在这个线程上耗时太长。选择最长的一段slice看看更多细节:

Fig. 6

Wall Duration指的是某一slice从开始到结束的时间,为什么取这个名字,是因为这个计时方式就好像是看着墙上的钟表一样。

Cpu Duration指的是cpu处理这一slice实际所用的时间。

需要注意的是,这些Duration所代表的意义差别很大。虽然显示这一slice从开始到结束花了18ms,而实际上cpu在这个slice上的耗时仅仅4ms。这有点奇怪,因此我们还是看下cpu在这段时间到底做了啥

Fig. 7

4个核看起来都很忙。

选择thread如红圈所示,这个thread来自com.udinic.keepbusyapp中。这意味着,一些应用占用cpu时间太长导致cpu负载过高,影响了你的app性能。

但这种情况不是很常见,因为其他app也不会总是在后台独占cpu资源。这些线程(占用cpu时间过长)可能是来自于你的app其他进程或者甚至是主进程中。毕竟Systrace只是个预览大致情况工具,在深入这些细节上会有限制。为了找到cpu为何负载过高,我们请出另一个重量级工具TraceView

Traceview

TraceView是个详细工具,可以展示每个方法具体的运行时间。先来看下Traceview的样子:

可以用Android Device Monitor或者代码来生成traceview,具体请参考这里

让我们先看下每一列代表什么意思:

Name:方法名,用不同的颜色来区分;

Inclusive CPU Time: 该方法以及子方法(该方法内部调用的所有方法)占用cpu总时长

Exclusive CPU Time:该方法占用cpu总时长

Inclusive / Exclusive Real Time:类似于Systrace里的Wall Duration

Calls+Recursion: 该方法总共被调用几次,包括递归调用

CPU/Real time per Call:该方法占用cpu平均时长。

我打开一个很难流畅滑动的app,启动trace,滑动一点点,然后暂停trace。找到getView()方法然后展开,如下图所示:

这个方法总共被调用12次,每一次平均占用cpu的时间大约是3ms。然而这个方法的实际运行时间是162ms,很显然有问题

看下它的子方法,我们能知道大概的时间分配情况。Thread.join()方法占用了差不多98%的Inclusive Real Time。这个方法的使用场景是,当前线程等待另一个线程的运行结果。而另外一个子方法是Thread.start(),由此我们猜想可能在getView()里面启动了子线程并且在等待其结束。

那么所谓的子线程在哪?

我们在getView()的子方法里看不到这个线程里面究竟做了什么。为了找到它,我搜索Thread.run()方法,只要是开辟新线程都会走这个方法。我一步步跟下去,直到找到根源:

我发现BgService.doWork()这个方法每一次调用大概耗时14ms,然后一共有40个这样的方法。getView()在里面可能不止一次调用了BgService.doWork(),所以导致它实际的耗时大概162ms。这个方法同时也使cpu负载过高的状态持续很长时间,你看下Exclusive Cpu Time,在整个trace中占用超过80%的时间。通过对Exclusive CPU Time排序可以快速找到哪个方法最耗时,这个情况在你处理性能优化问题时可能会遇到。

跟踪这些关键的方法比如,getView(),View#onDraw()等等,可以帮你定位app运行缓慢的原因。但有时候可能因为别的原因导致cpu负载过高,当cpu资源无法调度到ui线程时,就会影响界面的渲染流畅度。偶尔的gc,清除无用的对象,不会对前台运行的app造成很大的影响。但如果gc过于频繁则会导致你的app运行缓慢。

上一篇 下一篇

猜你喜欢

热点阅读