01
项目背景
“爱奇艺奇巴布”是爱奇艺为0-8岁孩子和家长定制化设计的寓教于乐平台,为儿童量身打造精致的观看体验,精彩内容解锁寓教于乐新方式。为儿童提供优质动画内容的同时,我们更关注APP用户体验。在产品交互设计上我们立足儿童视角,把内容浏览和观影做到做到简约易用。奇巴布APP整体界面简约、导航清晰、播放流畅,以极致的设计理念荣获2018年德国红点传达设计奖。
02
问题现状
卡顿原因
屏幕刷新率由硬件决定,通常是 60Hz/S,或者由 OS 和硬件协调动态调整刷新率。当程序通过 CPU、GPU 渲染画面的过程中,耗时超过一次刷新间隔,就会使得屏幕画面没有更新,导致卡顿现象产生,影响用户体验。
Feed卡顿问题分析
使用性能较差的手机滑动Feed流,通过Xcode-Instruments工具分析在滑动过程中具体耗时分布情况。截取代表性性能分析图,如下:


-
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中各种本地图片
-
预加载策略: 在APP首页渲染完成后,异步执行统计常用Card中依赖的角标和占位图,提前生成Bitmap, 并加载到内存。Hook图片调用方法[UIImage imageNamed:],在后续调用[UIImage imageNamed:] 方法时将省去I/O和解码过程,并通过以图片名键值匹配方式存储避免多次缓存。 -
解码方案:
-
图片库读取优化
-
其他优化
-
针对问题现状中第4个问题:Feed滑动过程中播放器开播与停播耗时较长,解决办法是在滑动时避免播放器的初始化,避免播放器的停播,维持播放状态与滑动前一致。另外在Card停播时显性的调用停止加载数据API,防止播放器在后台异步加载数据。 -
针对问题现状中第5个问题:Card区块曝光引发频繁Pingback投递耗时较长,且在主线程中操作。解决思路是滑动时对统计方法限流、降低调用频次。曝光精度会有非常小的下降,但在可接受范围内。
04
项目总结
技术总结
以UML时序图汇总优化技术点,如下图:
项目收益
使用Apple Xcode工具Scroll Hitch Rate监测优化效果,优化后每秒滑动挂起降低至1.4ms,远低于Apple对滑动流畅的标准5ms/s,即使在性能较差的低端机上滑动也非常流畅,用户体验有较大提升。

本文分享自微信公众号 - 爱奇艺技术产品团队(iQIYI-TP)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。