代码改变世界

iOS应用千万级架构:自动埋点与曝光

2020-07-10 12:02  jiangys  阅读(602)  评论(0编辑  收藏

背景说明

埋点需求是每个APP都需要做的一个功能,主要是为了统计分析数据。当然,也可以排查一些BUG,比如查看用户的操作行为轨迹,复现BUG。当一个项目比较庞大时,埋点功能也会跟着变得非常庞大,需要开发人员熟悉具体页面的业务逻辑,然后进行针对性的对组件的曝光进行计时,处理起来比较麻烦。本方案试图使用一种自动的方式,让埋点框架统一记录所有被关注的组件的曝光数据,以减轻开发人员的工作。

为什么要做曝光?

在电商里,曝光的统计与分析是非常有必要的。比如:需要统计商品的曝光率,一件商品展示出来的(曝光)出来了,但用户并没有去点击,这就可以统计出用户的曝光点击率,对后面的商品排序和展示优化分析是非常重要的。如果一个提醒按钮展示爆光出来了,用户没有去点击,可以分析到该功能的必要性。

方案说明

面对的主要问题

1. 曝光统计的代码和业务紧密耦合在一起,每个新的业务场景都需要重新开发

2. 业务数据不规范,提取数据比较麻烦

如何解决

1. 采用通用的方式监测组件可见性,不依赖具体业务

2. 复用组件的点击埋点数据,避免重复数据收集代码

 

调研发现系统API无法提供view可见状态的转换通知,但可以检查某个view是否在屏幕或者某个特定范围内,从而可以判断view的可见状态。

我们只需要维护一个关注曝光的view集合,定期刷新队列中每个view的曝光时长,就可以在离开页面时获取到曝光埋点所需要的数据。

整体流程

 

曝光监控驱动

1. 使用一个全局timer驱动,兼容性较好,可以支持所有的系统。

2. 如系统有帧刷新通知 ,注册系统的帧刷新通知,如iOS的CADispayLink,省去全局timer。

曝光监控驱动对监控队列中所有view的可见性检查,当view的可见性发生变化时,更新view对应的上下文数据。

3. 考虑到性能问题,利用scroll事件进行驱动

曝光监控队列的维护

1. view创建时,标记是否关注曝光。

2. 当标记关注的view所在页面被展示时,自动加入监控队列。

3. 当标记关注的view所在页面被隐藏时,自动移出监控队列。

4. 对于非资源类的曝光组件,因为不需要统计曝光时长,一旦确定曝光状态就移出监控队列。

曝光埋点发送

曝光事件触发时,进行埋点发送,发送方式使用批量发送。每个曝光组件,携带的业务数据集是点击数据集的子集,如果没有定义点击数据集的组件,使用common_set数据集。

方案存在的问题

1. iOS目前实现的可见性检查,没有判断同一页面上的view遮挡状态,主要考虑实现代价比较大,另外不是曝光埋点的主要应用场景。

2. 持续的曝光监控,可能造成性能问题,需要实测影响。

性能测试结果

因为公用一个Timer作为引擎驱动整个曝光监控模块,担心对整体APP性能造成冲击,主要是频繁的Timer回调中进行可见性检查对CPU的占用,因此分别在iOS和Android平台进行了针对性的性能测试。

测试设计如下:

以200ms为周期启动一个Timer用于驱动曝光监控,创建100个view,加入监控队列进行监控,统计曝光次数和曝光时长。

测试结果:

1. 假设view是资源组件,加入监控后一直不移除。在界面无其它交互状态下,CPU占用率<0.5。

2. 假设view是公共组件,一旦判断为曝光后可以移除监控。 在界面无其它交互状态下,CPU占用率几乎为0。

关键代码

1、将系统的didMoveToWindow方法和vsas_didMoveToWindow进行交换

2、当view加载到window时,加入VSVisibleChecker队列监听

3、埋点发送,构成一个VSStatisticsRecord对象-->将该对象异步存入数据库–>异步上传埋点数据–>上传成功,回调时,从数据库移除该VSStatisticsRecord对象