最新发布的 QQ 9 自上线以来,流畅度方面收获了众多用户好评,不少用户戏称 QQ 9 “傻快傻快”的,快到“有点不习惯了都”。
作为庞大量级的应用,QQ 9 从哪些方面做了哪些优化,使得用户能够明显感觉到流畅度的提升?本文将详细介绍 QQ 9 流畅背后的技术实现,以及在全流程做的性能优化探索,为应用提升流畅度提供可复用的经验。
01
仍有5亿人坚持用 QQ
今年是中国开启互联网时代的第 30 年,也是 QQ 作为“初代互联网产品”的第25年,手机 QQ 的第 14 年。
#仍有5亿人坚持用 QQ # ,正是有这群用户的坚持,督促着 QQ 技术团队不断的自我革新,为了能给用户更好的体验,对性能孜孜不倦的追求。
QQ 9 宣传图
QQ 9 开始,我们从底层架构自底向上全部重构优化,解决了手机客户端原来启动缓慢、容易卡、转菊花等待时间长、UI 跳变等一系列问题。上线后,收获了用户众多好评,其中有个高频关键词是「丝滑」,在丝滑的背后,其实是技术人吹毛求疵般的打磨。
本文将为大家揭开 QQ 9 背后的技术探索,分享 QQ 匠人们硬核的优化手段。
02
吹毛求疵的打磨
2.1 极致秒开 — 启动速度优化
QQ 的丝滑体验从「启动优化」开始,以 iOS 端为例,启动流程主要分为 3 个阶段:
- T0:点击图标到 main 函数开始;
- T1:从 main 函数开始到 didFinishLaunchingWithOptions 结束;
- T2:didFinishLaunchingWithOptions 结束到首帧渲染完成。
一般将启动过程按阶段分为 pre-main (T0) 和 post-main (T1 T2) 两个执行阶段:
- pre-main 阶段:系统 dyld 加载 App 镜像和初始化行为,与程序结构和规模关系较大。
- post-main 阶段:App 在渲染上屏前做的业务初始化行为,与具体业务逻辑关系较大。
一般工程上的优化方向:
- pre-main 阶段降低加载和链接的耗时:如动态链接转为静态链接,代码拆分组成动态库并进行懒加载。
- post-main 阶段减少主线程所执行的代码总量:如代码下架,代码执行时机延后或异步子线程化,代码逻辑执行效率优化等。
以下就这两个方向,介绍一下 QQ 本次做的有亮点的地方。
2.1.1 pre-main 阶段 - 按需装载代码
动态库懒加载方案原理图
代码拆分组成动态库并进行懒加载这项技术多应用于业界大型 App(抖音、Facebook、快手)中,但 QQ 的业务复杂度颇高,直接使用业界方案无法满足我们的需求。经过一番探索我们找到了一些创新技术点:
- 使用 __attribute__((objc_runtime_visible)) 实现低成本代码动态化改造。
- 使用 objc_setHook_getClass 实现动态化代码入口收敛,保证了方案稳定性。
最终在 QQ 9 中大规模的应用实现了对 pre-main 阶段的启动耗时优化(这个技术方案约贡献了33%左右的启动总耗时优化数据收益):
Xcode Organizer Launch 数据图
2.1.2 post-main 阶段 - 线程治理
我们防劣化系统监控到主线程抢占的问题越来越严重,通过 Instruments 查看,我们发现一些严重的情况下,温启动过程中主线程有 14%的时间片处于被其他线程抢占的状态。