这个页面好卡【Android性能优化篇-如何选择合适的工具定位问题】

一、序

“这个页面在干嘛啊,咋卡成一坨屎了。”
“这个页面在这台魅族机器上居然卡成这样了,魅族手机咋还不停产啊”。
“用户反馈说在 ta 的机器上某个页面体验不流畅,我手机挺好的呀,用户手机太差了,公司出点钱给用户换个手机得嘞”。
“... : (”

我们经常会听到这样“有趣”的“吐槽”。页面卡顿,这是在Android开发过程中经常会遇到的问题。引起卡顿的原因很多,定位的方法工具也很多,从何下手,怎样寻找正确的突破口,准确定位问题,做优化呢?

我想以这周优化「云课堂 C 版首页,类目列表页在魅族3机型上显著卡顿问题」为 切入点,和大家一起探讨下,面对 Android 性能问题(如页面卡顿这类)时,我们怎么选择合适的工具来定位问题。

如果阅读时间有限,可以重点关注第三章节「方法篇」


二、实战篇

2.1 背景

K12 Android 开发同学在接曝光打点统计的时候,发现接入曝光打点后,页面会有些卡顿,特别是在魅族3手机上,卡顿简直无法忍受。收到反馈后,测试了云课堂 C 版和 B 版线上版本,发现两个 APP 的在有曝光打点统计的页面(如首页,类目列表页面等),在魅族3手机也是非常卡顿,卡顿级数还是比较大的那种,属于比较难滑动的体验。

2.2 定位问题,并解决问题

1)首先初步定位问题,圈地分析
是否真的主要是曝光打点统计引起的页面卡顿。在注释了曝光统计打点代码后,发现首页和类目列表滑动流畅很多。基本定位“犯罪嫌疑人”:曝光统计打点

先解释下「曝光统计打点」:这是个业务需求,即客户端需要统计首页滑动过程中曝光了哪些课程、信息的数据,以及其它页面类似的曝光统计需求。

2)进入圈地,阅读代码,推敲代码逻辑
仔细阅读了曝光统计打点的代码,发现主要是这4步:

  1. 曝光时机。
  2. 准备曝光上报数据。
  3. 开启线程,写日志文件(考虑线程安全,才用了synchronized)。
  4. 日志文件积累到一定的标准,服务端上报(异步)。

那么第4步,可以基本排除,是异步线程,并且线程池用的是newFixedThreadPool,默认用5个线程;第三步,用了同步机制,怀疑是否会有问题,看了下线程池用的是newSingleThreadExecutor,

无边界的队列,当快速滑动,需要大量曝光数据上传时,线程排队,消耗的时间也不影响UI线程。故基本也排出。(这边只是排除引起卡顿的原因,并不代表这种方式就是合理的。)

好了,就到第2步和第1步。

其中,第一步,在曝光打点代码注释前后,都是存在并且运行的,故影响也不大。那基本落在第2步了。

目前卡顿的问题,因为不涉及UI,基本可以缩小至:

  • 第2步中某个地方,耗时过长
  • 曝光打点是否引起内存泄漏,在某些场景下,导致内存占用非常大,引起GC,进而影响帧率,用户感知卡顿。

3)寻找工具分析,并定位:
根据上述分析的,目前需要的工具便是内存分析工具和方法耗时工具了。

「内存分析」
根据上述的分析,凭直觉,曝光打点引起的内存问题应该不大。但谨慎起见,还是做了下简单的分析,工具分析结果表明,有曝光打点和无曝光打点前后的内存情况差不大,但这里倒发现了非曝光打点确实存在一定的内存泄漏问题,这个问题就另起优化再做讨论。
基本排除内存方面的问题,那就是方法耗时需要重点关注了。

「方法耗时分析」
以下是traceView分析的结果:

 

很意外的发现:configCommonParam这个方法在这个手机上居然耗时2-3s,其中,deviceIp和localIpAddress是元凶。

然后分析代码发现,configCommonParam是放在UI线程的。最直接的先将这个方法放入异步线程中,重装APP,卡顿现象明显消失。好吧,就这样好了么?那显然不是,我们看下,为啥这么耗时,这个数据应该是个全局参数。

然后再看代码,发现参数的获取没有进行缓存,都是每次取,每次算,其实对于这类参数而言,可以简单缓存下,每次获取的时候除第一次需要计算,别的只要读取缓存即可。又稍微改了下,做了单例模式,缓存存取。再次用工具分析,耗时直接变成几ms了。

其实,没改几行代码,但带来的用户体验,截然不同,甚至,会觉得,这个结果好出乎意料,参数准备,获取个deviceId也会引起性能问题??但有时候,事实就是很残酷,也是防不胜防,故要求我们开发过程中,多考虑细节,时时刻刻牢记细节。

简单归纳下修改点:

  • configParam放入异步线程
  • deviceId类似参数做了一级缓存(内存缓存)

2.3 其余优化

简单说下,与本次主题关系不是很大,比如,曝光打点的第三步,如每次有曝光数据就去文件io操作,写文件,其实可以增加一层,内存缓存,当达到一定数量时,再去进行文件操作。


三、方法篇

一般遇到的卡顿问题,我们通常会有这几方面的考虑

  • 是否是UI引起的?
  • 是否是内存泄漏引起的?
  • 是否是方法耗时引起的?

定准大方向,然后用不同的工具进行分析。

3.1)「UI分析工具」

  • 过渡绘制监测,
  • 帧率检测,
  • 层级检测(Hierarchy Viewer ,lint等)

推荐两篇google官方文档:

3.2)「内存分析工具」
内存分析工具,Android Studio自身带的Monitors一般情况下够用了google官方介绍
基本有这几步:

  • 需要分析的场景下,是否有内存逐渐变大的趋势
  • 若有,则手动触发GC后,再次操作需要分析的场景,然后dump下来,分析
  • 内存分析,观察哪些对象占比很高,再查看其引用情况

也可以通过敲命令行,观察logcat日志,然后dump heap,分析内存。

3.3)「方法耗时分析工具」

方法耗时工具基本就这几个:systrace,viewTrace,还有就是自行打日志。
自行打日志,需要非常清楚范围,否则如大海捞针。

那么是选systrace,还是viewTrace呢,尽管目前提倡的是systrace,但我觉得,哪个熟练用哪个,那两个都不熟练呢?先用viewTrace吧,比较简单。

至于这些工具,怎么快速操作,怎么能够一下子就看到问题,就好比是:医生拿着X光片,或者别的单子,经验足的医生能够快速辨别疾病。
没有什么别的方式:无他,唯熟尔


四、尾

我想呼应下,在我上篇文章从细节处谈Android冷启动优化中的一个主题思想「细节」。任何的性能优化,都是注意细节,追求精益求精的过程,如古人之云“天下大事,必作于细”

同时,我们解决问题的整个过程,也是知识体系的一个形成迭代过程,一般需要经历如下几个阶段:

「问题」,「解决」,「沉淀」,「应用」

细化如下:

  • 遇到问题 ->
  • 搜索能够解决问题的新知识、新方法 ->
  • 利用已有的知识,研读、理解新知识、新方法 ->
  • 掌握新知识、新方法,并解决当前问题 ->
  • 提取新知识、新方法,纳入到自己的知识体系,强大自己的知识体系 ->
  • 再次遇到问题,快速条件反射。


网易云新用户大礼包:https://www.163yun.com/gift

本文来自网易实践者社区,经作者韩坤芳授权发布。