一次艰辛的问题定位

Google 在今年初在欧盟区需要执行新的数据隐私政策,继而对 app 中就如何使用用户数据,在安装后的初次运行时需要展示询问框。现有的广告集成 SDK 做了相关的便利性工作,可以与 Google 自家的 UMP SDK 协同来自动化处理这一流程。要求 app 提交符合新政的截止时间是大约十天前。

由于集成的说明文档相当简单,所以没把这个工作想象得有多么复杂。很快就完成了例行工作,给出了安装包。然而,负责业务的小伙伴在即将上架前夕发来一个问题描述,在某种情况下,app 会闪退。奇怪的是,这个情况仅为个例,相关包括老夫在内的其他人均未能复现这一情况,而且,云端的崩溃收集平台上没有任何体现。

这个问题的暴露组合前提是:在仅当 WiFi 连通(但同时不能存在 VPN,而且,不管那个 VPN 通不通全球网)的情况下,在 Google Pixel 6a 手机上,操作系统为 Android 13 时,app 从 Splash Screen 跳转到主界面后,静置十余秒,会突然消失,俗称闪退。由于表现比较怪异,所以老夫断定极大可能是疑难杂症,排查会需要不少时间。所以就让一开始执行集成操作的小兄弟去搞 iOS 上的相同工作,自己来接手这个烫手山芋。

如果网络可以连通全球网,不会出这个问题;如果是移动网络,不会出这个问题;如果完全没有网络,不会出这个问题。加之集成 UMP 流程的代码改动实在算不上大,所以一开始就推定问题可能不是出在自有代码中。当然了,要拿出令人信服的证据。

通过对 logcat 在事故现象机上的记录,发现 app 并不是自行 crash 而闪退的,而是因为收到了系统发给它的 signal 9(kill)而退出的,换句话说,它是被系统杀死的。在上下相关的记录中查看,进一步发现了被杀死的原因,是“Too many Binders sent to SYSTEM”,其数量超过了 6000,这个阈值是写死在系统实现中的。结合它的表现跟网络环境有关,可以推测是因为当网络处于一种似通非通的状态时(我们的常态),某些组件以高频发起了过多的网络请求但又无法触达目的地而产生拥塞所致。也许某些 SDK 的实现里有潜在的 bug。

根据网络搜索的一些信息,对 firebase 的 crashlytics 组件产生了怀疑。将现象机借来,对该组件进行了版本降低的编译尝试,效果立显。手到病除,那一定是开心不已。于是把新包发出,现象机归还。可惜打脸来得太快,当晚合作伙伴就把现象机重新启动了下,新旧包交替安装了几次,就发现新包相较老包并无改进。不得已,只好将现象机再次借回,继续攻关。对 firebase crashlytics 得版本一降再降,甚至改变了直接指定各个组件版本的方式,改而采用 firebase BOM 组件来自动控制版本,也无济于事。

考虑到实际项目的代码规模较大,且其业务代码也能对问题排查有所干扰,决定克隆出一个包名相同,仅余 Splash Screen 以及主界面的空壳 app,但是保留所有的第三方依赖的集成。用了两三天才拆离完毕,编译通过。此后一步步将不同用途的 SDK 的初始化代码屏蔽掉,例如支付、统计等等,直到仅剩广告相关的 SDK,问题依然可以稳定重现。

期间还有一次虚假反馈。由于 UMP 流程的要求,广告 SDK 的初始化代码有所调整,当初做调整的时候对一个地方的调用顺序即有一点不踏实的感觉。在测试过程中发现,如果对那个地方的一行代码进行屏蔽的话,问题就会消失。得到这个结果的时候喜忧参半,因为那行代码涉及到广告的预加载时机,另找合适的地方要通盘考虑,不是可以一蹴而就的,但如果它确实是病灶就太好了。晚饭后在家里把代码拉下来继续测试,突然发现即使屏蔽了它,问题也仍会出现。不由得又是一阵失望袭来。

好在,剩下的可以折腾的东西已经不多了。最后把目光放在广告集成平台的各个 adapter 上来。数量属实不少,只好硬着头皮过,挨个屏蔽,然后测试。第一遍过后,有两个疑似目标,再过一遍,就只剩下一个了。针对它又反复开启、屏蔽了若干次,最后可以基本认定,它就是罪魁祸首。是 InMobi 的。

在所有的尝试工作中,写代码、捋思路这些都不是最痛苦的,最痛苦的是,做了改动要看效果的话,gradle 构建脚本重新同步需要好几分钟,而构建安装包又要好几分钟,最神奇的是,一秒钟前构建好的安装包,立刻让它运行于设备上的时候,构建过程会重新来过,而且丝毫也不节省时间,令人郁闷。

虽说最终这个结论还需要合作伙伴的认可,但事实上应该已经是告一段落了。

后附几篇与此问题相关的网络资料。

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注