闲鱼在业务的快速迭代过程中,app 的长列表滑动流畅度逐步恶化,对用户浏览内容体验产生伤害。闲鱼作为国内 flutter 应用的先驱,APP 以 flutter 和原生 Native 的混合工程存在。这里分别就 Android 原生、flutter 页面和大家分享我们的优化思路。
本文分为三个部分:
流畅度指标和检测工具构建
原生 Android 长列表优化
flutter 长列表优化
流畅度优化整体思路图如下:
2 流畅度指标和检测工具构建2.1 现状和难点
检测工具现状:以 Android 为例,现有流畅度工具可分为:
侵入式
集成 sdk,通过注册帧回调计算流畅度。Android 见 Choreographer 类
profile 模式
无侵入式
执行系统命令,如
adb shell dumpsys gfxinfo ${packageName}
腾讯 GT APP,底层执行
service callSurfaceFlinger1013
,高版本 Android 已不支持
流畅度指标现状有:
FPS (Frames Per Second)
SF(SkippedFrame,跳帧) app 在单位时间 1 秒内,跳过执行 Choreographer 中 doFrame 的次数
SM(Smooth,流畅度) app 在单位时间 1 秒内,实际执行 Choreographer 中 doFrame 的次数。其中 SM=60-SF。
帧耗时数据 使用 adb 命令得到几个关键分位的帧平均耗时:
然而以上工具和指标定义在 app 的复杂场景下,尚存在问题
多平台问题
现 APP 技术有原生、h5、小程序、RN、weex、flutter 等。暂无一款无侵入的流畅度检测工具能同时支持多个平台、多种机型和多个指标数据,而侵入式的检测工具无法检测竞品 APP。
指标选择和用户体验一致性
我们期望能有少量的几个指标数据,准确的表达用户流畅度体感。平均 FPS(SM 和 SF 类似),不足以反映用户体验。如相同 30 FPS,可以是 1s 内 30 个 33.3 ms的画面,也可以是 29 个 16.6ms 的画面再加 1 个 516.9 ms 的画面,但用户体验并不相同。
流畅度数据影响因素多
滑动速度和滑动状态:idle(停止)、drag(手指拖拽)、fling(自由滑动)都是影响流畅度数据的重要因素。
2.2 流畅度指标制定
维基百科中动画定义:一种通过定时拍摄一系列多个静止的固态图像(帧)以一定频率连续变化、运动(播放)的速度(如每秒16张)而导致肉眼的视觉残象产生的错觉——而误以为图画或物体(画面)活动的作品及其影片技术。
列表滑动同理,是 APP 以一定频率(60hz下16.6ms)和不同 offset 计算出一系列静止画面,让肉眼看到滑动动画。
当我们说列表滑动不流畅,是因为频率过低无法让肉眼产生视觉残留,或在时间(画面停留时长)和空间(画面内容)产生跳变,让用户感知到变化的不自然。以此我们可以定义指标如下:
时间角度
定义平均 FPS:定义一次检测的平均帧率。反应画面平均停留时长。
定义 1s 大卡顿次数:平均 1s 内出现占用 3 帧及以上的画面次数。反应画面停留时长跳变
空间角度
offset 跳变值:在画面不掉帧的情况,若其中一个画面出现跳变,甚至花屏或者绿屏会让用户体验到不流畅。在 APP 滑动过程中,画面内容由 offset 决定,而 offset 跳变,和卡顿时长、差值器实现均有关联,现有差值器实现基本基于 D/T 曲线(距离/时间),为此平均 FPS 和 1s 大卡顿次数很大程度上体现了画面跳变,同时考虑到无侵入式检测 offset 的难度问题,暂不考虑 offset 跳变值。
综上,我们定义流畅度指标为平均 FPS 值和 1s 大卡顿次数。
2.3 流畅度检测工具实现
我们从 APP 录屏画面入手,计算流畅度指标值。当我们得到 APP 滑动过程中的录屏数据,可通过每 16.6ms 检测录屏画面是否发生变化,当连续画面未发生变化,则表示发生了卡顿。无变化的连续画面数则表示了卡顿的时长。
为得到目标 APP 录屏数据,检测工具 APP 向系统注册录屏服务,然后在检测工具 APP 的帧回调中不停读取录屏画面,并和上次检测画面 hash 值进行比对。
检测工具 APP 和目标 APP 进程隔离,为此目标 APP 发生卡顿并不影响检测工具 APP 的帧回调
为保证每次录屏画面读取和 hash 值计算在 16.6ms 内完成,需根据高低端机型调整画面宽高压缩比。