近期遇到了一个很棘手的问题,我们自身统计的 iOS App 日活和第三方统计(友盟)相差很大,少了一倍多,安卓的统计则差不多。
因为日活数据的准确性很重要,领导比较重视。
为了排查和定位问题,我们实时了各种手段:前端代码打点/异常分支打点、后端接口打点、Nginx IP/日志分析、第三方日志打点、用户行为数据分析……耗时一个多月,最终发现问题的元凶竟然是……
按照正常的理解,用户打开 App,使 App 切换到前台(不管打开多少次)即算当天一个日活(UV),如果一直处于后台(对用户不可见)是不能算的。
一般计算是按照设备 ID 去重,例如昨天 100 UV 代表昨天有 100 个不同的设备访问,但是真实的人数可能只有 90 个,因为有些人有多个设备。
还有一种算法是按照登陆用户数算,一个人在不同的设备登陆同一个帐号只会算一个日活,但是因为游客没有帐号,所以所有的游客会被算到一个日活。
常用的是第一种算法,按每日设备数计算。
根据 iOS 的生命周期定义,当进入 applicationDidBecomeActive
方法时,即代表 App 被前台激活,此时往服务端上报一个埋点,该埋点包含了用户的设备信息、基础信息等。
为了确保埋点传输的可靠性,需要做一些策略保障,例如缓存、失败重传等。
这块的代码和逻辑实现,经过多次确认是没有问题的,做过的尝试:
applicationWillEnterForeground
加入埋点,提高容错性这个是个黑盒,其客服也没有一个很明确的答复。不过,从线下的测试看,它的计算也是比较准确和实时的,没有发现什么可疑的地方。
在分析接口日志的过程中,发现某些接口的 UV (A)很高,但是埋点接口的 UV (B)很低,通过分析 Nginx 日志的 IP 数也可以确认这一点,也就是说有很多用户(A - B)没有触发埋点接口,但是触发了其他接口,什么情况的用户(C)会不经过 App 的生命周期方法而访问接口呢?
进一步的,对请求 UV 高的接口(A)增加了一些调试信息,包括用户的环境信息、状态信息等,进而发现 C 群体用户的 App 状态基本都是 Background,也就是在后台发送的!此时有一种大胆的猜测,App 会被后台激活?!
基于上面的问题和猜测,进行验证:
实验结果,第一天的统计结果都是正确的。但是第二天早上,发现第三方竟然有统计结果(其中5 台设备,统计了 2 个 UV),经过复查 Nginx 日志,确实发现有 2 个设备在后台请求了某些接口,而不经过埋点接口,所以第一方统计的 UV 是 0。
也就是说 App 被后台激活的时候,能够触发第三方的日活接口,但是没有触发第一方的埋点接口,所以导致了两种统计的误差。
此时我们将焦点放到「后台应用刷新」上,发现这是一种系统行为,App 并不能显式关闭这个机制。
网友有一些讨论,这种机制是 iOS 根据用户行为习惯自学习的(理解是一种预加载、预刷新的机制),并没有具体细节,也没有找到特定的执行套路,经过验证也并不和推送强相关,比较玄学。
那么问题来了,你家的 App 是不是也是日活虚高呢?
Copyright © 2015-2022 BY-NC-ND 4.0