背景:
随着版本迭代,功能增加,「最近会话」列表在低端机滑动列表时开始有卡顿的现象。
而这个列表是用户重度使用的页面,所以进行性能优化、改善卡顿就很重要了
卡顿的原因:
从业务层的角度,卡顿主要原因有:
1、主线程任务过于耗时
2、view树的measure、layout、draw流程过于耗时
3、GC 时 Stop The World。频繁的GC也就频繁的卡顿,也就是 内存抖动。这是较少人关注到的一点。
对于第2点,可以优化布局、减少层级。这是基本技能,就不展开了。
对于第3点,实测我们APP的该列表即使频繁快速滑动 触发bind(),GC的频率也很低。所以也不展开
所以,本次卡顿主要围绕 主线程任务过于耗时 展开优化。
具体执行步骤:
1.有的放矢:
用工具准确定位 「造成卡顿的主因」
2.执行优化:
对卡顿主因进行优化
3.复盘:
对优化前后进行 「记录和对比」
1、用工具准确定位 造成卡顿的主因
1.1: 用什么工具?
我选择AS自带的CPU Profiler
网上的文章很多都提到 TraceView、Systrace,但是你很容易在Android Dev官网发现,Android已经不推荐这些旧有的方式了(TraceView废弃、Perfetto替代Systrace)。
而对比Perfetto和Profiler,我选择了功能强大、实时性更强的Profiler(其实profiler也是利用了其他已有的工具如System Trace,缝合进as里)。
1.2:查看卡顿情况
来到AS最新版本 Clipmunk,在CPU Profiler下,首先选择「System Trace Recording」,
点Record按钮,然后快速滑动屏幕。Record完成后,查看一下卡顿情况
红色的就是卡顿帧。可以看到,在低端机上,卡顿还是很严重的。(在稍微好一点的机子上其实基本没有Janky帧)
放大局部区域:
可以看到,耗时主要花在 View#draw()、RV#onBindView()。而具体是draw()的哪里呢?「System trace」帮不了我们,正如其名,它只能检测System侧的调用
接下来需要用到Profiler 的「Method and function traces」功能
对于Clipmunk版本,几个选项我都使用了一下,觉得还是标记为(legacy)的选项比较趁手:
重复以上步骤:点Record后快速滑动列表。完成后查看结果(在结果页中 点选「main Thread」、Flame Chart火焰图)
方法耗时情况一目了然。
以我这里为例,耗时大的****method,主要有截图中的标注为 3 、 4 等等的方法(getChatListTime()、isMute())
2、执行优化
具体怎样优化,要以各位的应用的具体情况而定。
图中的3 :
主要耗在 bind()下面的 getChatListTime()的方法,具体是对时间戳进行format处理。
原来的处理算法,的确是有点效率问题。所以这个问题的解决方案是 优化算法效率
实测优化后的耗时仅仅是原来耗时的 1/7
图中的4 :
主要是 bind()下面的 调用第三方SDK的一个叫isMute()的方法。
这里大可不必每次onBind()都向第三方SDK查询isMute()。我们可以把查询的结果缓存下来。当然,要做好Mute设置改变后的通知和刷新机制。 所以这个问题的解决方案是 设置缓存
改善了上面两个业务相关的耗时方法,现在压力来到了Glide****相关的图片加载方法。
这里我的对策是:列表滑动时不加载图片只展示占位图,列表停顿时才加载图片。姑且认为是 业务降级 的策略吧
把上面的主要耗时方法都优化后,卡顿情况改善了一个档次:
对应地,main Thread 的火焰图,onBind()耗时所占的百分比,已经比较低了
最后的最后,优化itemView布局,减少视图嵌套层次。这个收益也很明显:
3.复盘:优化前后进行对比
优化前卡顿帧:
优化后卡顿帧:
效果很明显,优化后卡顿帧只是偶尔出现(并且大部分卡顿是在同时处理 Touch Event时的帧才出现)。