iOS 秒开检测方案总结

567 阅读4分钟

背景

  • 用户从点击一个按钮,到新页面展示内容的这个时间,是一个很重要的性能指标。很多人对一秒法则有所了解,指的是在WIFI或4G的网络下,一秒内能够完成一个页面的渲染。那么应该如何检测页面的秒开率呢?

页面打开流程

截屏2022-10-22 11.46.02.png

  • 用户打开一个页面时,页面通常会经历如下步骤:
  1. 容器初始化
  2. 数据读取(本地缓存或网络请求)
  3. 数据处理 (序列化、模型化处理)
  4. 渲染页面(GPU绘制页面首帧)
  5. 图片加载(通常会异步加载图片)
  6. 渲染图片
  • 那么在1-4步骤间,用户在屏幕内,一般没有可见的有效信息,此阶段通常是展示loading的一个过程。

还有一个现实问题

  • 秒开检测存在一个很实际的问题,就是原生/RN/H5/Flutter很难使用一种通用的技术来进行秒开检测。
  • 大多数时候,我们是对不同生命周期进行埋点统计,但是这种埋点统计存在各技术栈之间实际统计的开始时间和结束时间不一致的问题。 截屏2022-10-22 11.54.56.png

开始调研

  • 首先确认一下我们的需求:
    1. 一套方案统计所有技术栈秒开
    2. 业务不介入
    3. 能获取较为准确的秒开时间
  • 经过一番调研,发现各厂商方案都存在业务介,多技术栈存在不同方案的问题。今日头条H5的方案,比较适合我们的需求。今日头条品质优化 - 图文详情页秒开实践 截屏2022-10-22 12.19.07.png
  • 头条的方案是延时1s进行截图,并分析白像素占比,如果存在可见内容,白像素占比应该会降低,那么只要设置一个白像素阈值,低于该阈值即可认为秒开成功。
  • 另一个问题,头条的方案无法较为准确的获取秒开时间。

设计一套满足我们需求的检测方案

截屏2022-10-22 12.30.53.png

  1. 从页面ViewWillAppear生命周期为起始,在1秒内每间隔100ms执行一次截图,在1-2秒内每间隔200ms执行一次截图。
  2. 对每次截图进行分析,看是否达到秒开阈值,若达到则停止检测,否则一直检测到2s结束。

方案优点

  • 无论是原生/RN/H5/Flutter,我们都可以获取ViewWillAppear回调,及检测开始时间应该是一致的。
  • 相较于1s截一次图的方案,我们能明确的知道秒开准确时间节点落在哪个时间段。

性能问题

  • 截屏的原理是首先创建一张被压缩画布,然后将view绘制在这张画布上。
[view drawViewHierarchyInRect:CGRectMake(0, 0, scaleWidth, scaleHeight) afterScreenUpdates:NO];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
  • 压缩画布的作用是减少绘制的性能,越小的画布,绘制的时间越少。绘制应放在一个常驻子线程上。

一些优化

  • 每次截图前可以先检测页面可见区域内是否存在Loading控件,如果存在,立刻停止本次检测,直接等待下一次秒开检测,直到没有检测到loading控件,再进行截图策略。
    • 通过hook addsubview匹配到页面loading控件,在每次检测时,判断Loading的状态和可见性即可。
  • 实际场景可能不止白色一种底色,那么在像素计算前,应该进行底色的判断。
    • 可以通过获取截图的几个定点位置的像素颜色,来确认底色。

如何进行秒开优化

前端、后端

  • 降低请求量:合并资源,减少 HTTP 请求数,minify / gzip 压缩,webP,lazyLoad。
  • 加快请求速度:预解析DNS,减少域名数,并行加载,CDN 分发。
  • 缓存:HTTP 协议缓存请求,离线缓存 manifest,离线数据缓存 localStorage。
  • 渲染:JS/CSS优化,加载顺序,服务端渲染模板直出。

客户端

  • 预加载WKWebView
  • 模版抽离

秒开优化参考

参考资料