01
项目背景
“爱奇艺奇巴布”是爱奇艺为0-8岁孩子和家长定制化设计的寓教于乐平台,为儿童量身打造精致的观看体验,精彩内容解锁寓教于乐新方式。为儿童提供优质动画内容的同时,我们更关注APP用户体验。在产品交互设计上我们立足儿童视角,把内容浏览和观影做到做到简约易用。奇巴布APP整体界面简约、导航清晰、播放流畅,以极致的设计理念荣获2018年德国红点传达设计奖。
在技术侧我们不断完善技术架构体系,优化提升APP各项性能指标,发挥工匠精神把APP的用户体验做到极致。优化技术方向包括:APP冷启动、Crash防控、内存管理、电量消耗、安装包体积等。奇巴布APP历经数年迭代开发,产品功能不断完善,叠加儿童移动设备换新频次相对成人低,奇巴布APP首页Feed流在老旧设备滑动使用过程中会出现卡顿情况,滑动中有视频播放等场景时页面卡顿比较明显,影响用户体验。
02
问题现状
卡顿原因
屏幕刷新率由硬件决定,通常是 60Hz/S,或者由 OS 和硬件协调动态调整刷新率。当程序通过 CPU、GPU 渲染画面的过程中,耗时超过一次刷新间隔,就会使得屏幕画面没有更新,导致卡顿现象产生,影响用户体验。
Feed卡顿问题分析
使用性能较差的手机滑动Feed流,通过Xcode-Instruments工具分析在滑动过程中具体耗时分布情况。截取代表性性能分析图,如下:
Hitch 出现时的耗时调用堆栈
滑动列表时的统计曝光耗时堆栈
Feed卡顿问题分析总结如下:
Feed中存在较多不同样式Card,多种Card类的合计初始化耗时较长;
Feed中有各种图片,这些本地图片资源在主线程读取和渲染,有性能提升空间;
Feed滑动中图片库(基于SDWebImage二次开发)在读取磁盘缓存文件过程中耗时较长,且在主线程中做读取操作;
Feed滑动过程中播放器开播与停播耗时较长;
Feed滑动过程中Card区块曝光引发频繁Pingback投递,整体耗时较长。
03
解决方案
Card数据解析与渲染异步处理
使用QoS的NSQualityOfServiceUserInitiated创建自定义队列,当前设备CPU活跃核心数和自定义最大核心数比较,获得结果设置队列上限。通过限制并发数量减少多个线程频繁切换带来CPU资源损耗,避免GCD API频繁创建线程。Card数据解析和布局计算、播放器内核初始化、图片和文本渲染都使用该自定义队列来处理。
首页Card预加载
在奇巴布Feed列表中前几屏幕业务数据大多为固定推荐内容组合,Card类型繁多无法复用,因此预先加载前几屏不同Card实例是提高性能的一种技术手段。具体解决方案如下:
预加载策略:
APP每次冷启动时,总体策略是以最快速度展示内容给用户,缩短启动时间。首页Feed接口在发送网络请求过程中有网络延迟,在服务端数据传送到本地之前加载本地磁盘缓存的上次启动时Feed流JSON数据。利用磁盘中持久化的JSON数据,解析出JSON对应的Card类,将常用的Card类名缓存到内存,以备在合适的时机初始化Card实例,以备后续滑动Feed时直接读取Card实例,从而避免在滑动过程中初始化。
预加载时机的判别与执行:
在用户启动APP的瞬间,会存在大量异步业务操作抢占CPU资源。所以需要寻找CPU闲置且用户没有手势操作时预加载Card。监测与执行大体实现逻辑是:通过向主线程RunLoop添加Observer监听DefaultMode,监听作用的时间点为线程进入休眠之前或RunLoop即将退出。当方法回调触发时,意味着用户没有滑动或者滑动结束,在回调方法中做预加载。
本地图片预解码
Feed中各种本地图片
Feed中不同类型Card中的角标和占位图,这些图片为本地资源,在用户滑动Feed时,过往实现方式是在主线程中从磁盘读取资源,然后进行渲染。将这些I/O操作放入子线程,在图标使用前提前生成Bitmap到内存中,可以减少用户在滑动过程中因加载图片带来的性能损耗。具体解决方案如下:
预加载策略:
在APP首页渲染完成后,异步执行统计常用Card中依赖的角标和占位图,提前生成Bitmap, 并加载到内存。Hook图片调用方法[UIImage imageNamed:],在后续调用[UIImage imageNamed:] 方法时将省去I/O和解码过程,并通过以图片名键值匹配方式存储避免多次缓存。
解码方案:
强制解码过程:iOS15.0以上系统直接调用系统方法imageByPreparingForDisplay进行预解码,其他系统版本则使用 CGBitmapContext 强制解码。
图片库读取优化
图片库设置组件的图片策略是通过异步下载URL对应图片并做缓存。本次优化主要是针对已经加载到缓存的图片,当对应Card再次滑入屏幕时,第一时间加载缓存中图片而不是再走图片库内部方法各种业务逻辑判断。
其他优化
针对问题现状中第4个问题:Feed滑动过程中播放器开播与停播耗时较长,解决办法是在滑动时避免播放器的初始化,避免播放器的停播,维持播放状态与滑动前一致。另外在Card停播时显性的调用停止加载数据API,防止播放器在后台异步加载数据。
针对问题现状中第5个问题:Card区块曝光引发频繁Pingback投递耗时较长,且在主线程中操作。解决思路是滑动时对统计方法限流、降低调用频次。曝光精度会有非常小的下降,但在可接受范围内。
04
项目总结
技术总结
以UML时序图汇总优化技术点,如下图:
项目收益
注:Scroll Hitch Rate表示图像帧延迟显示在屏幕上的时间总和 Hitch time除以用户滑动屏幕的持续时间 Scroll duration 得到一个比例,它可以反映用户感受到的滑动卡顿的严重程度。
也许你还想看
Prometheus监控指标查询性能调优
爱奇艺DRM修炼之路
组件化设计在会员业务的应用和实践