1. 背景
在近期 iOS 上线的版本,友盟在它的升级版本中默认就自动进行用户的崩溃收集上报。
如果只是恶心一下开发也就算了,然而在发版本时,却发现友盟的代码没有对上报的数据做类型的安全判断,导致读取数据每次都会 crash。
搞崩溃检测的,自己却导致 App 不断崩溃
堆栈如下:
从方法名称 [UMCrash initUMCrash:channel:] 和 [WPKSetup sendAllReports] 可以很容易看出来, 这是友盟在做初始化的时候,对崩溃信息进行了一次检测,然后做上报,在处理数据的时候挂掉了。
看不到具体代码,控制台看到直接原因是
[NSTaggedPointerString objectForKey:]: unrecognized selector sent to instance
由于友盟的初始化发生在很早的时候 ( 我们一般都放在启动阶段 )。这就导致大多服务还没起来,应用就已经崩溃了。只要出现了这种情况,每次打开 App, 都会因为一样的问题,而连续闪退。
2. 连续崩溃的后果
那么像这样的连续崩溃,会造成什么后果呢?
可以总结为以下的 3 点:
3. 解决方案
面对上述的情况,因为掌握可复现的场景,通过技术手段,我们的确可以 hook 掉它的上传方法,从而解决崩溃问题。
例如这次,对我们发生的路径中的 [WPKSetup sendAllReports] 进行截获,不再执行。那么它当然不再崩溃。
但是上面解决方法有 3 点问题:
在前面有提到过,连续崩溃的一大问题是--开发无感知。
也就是说,我们连问题发生了都不知道,所以首要做到的是发现问题。
通常最先想到的思路,就是和崩溃上报框架一样,通过捕获异常,来观察它的每次崩溃。
捕获异常的操作,也存在两个缺点:
对于第二点与第三方崩溃收集框架的冲突,是影响最大的地方,因为他们的代码通常对我们来说都是看不到的。
而在微信读书团队的 iOS 启动连续闪退保护方案 一文中,为我们提供了很好的思路:
通过 crashCount 来代表崩溃次数,每次启动的时候让它加 1,如果 App 存活过一段时间,那么证明没有发生连续崩溃,将它置空复位。一旦超过我们设定的次数阈值,证明连续这几次时间阈值内,都没存活过去,发生了异常崩溃。
当然也存在误报的情况,比如用户在这段时间阈值内,主动杀掉 App。这一点通过调整次数和时间两方面的阈值,可以控制。
我们可以在原来的方案中,更进一步控制误报,想办法监听用户主动杀 App 的场景:
对于误报的情况,大多数都是第一种,在几秒之内,启动时前台杀 APP,iOS 中通过 UIApplicationWillTerminateNotification 来监听,收到通知后,将次数置空清零。
要对于崩溃进行修复,首先需要知道这类问题的常见原因。
对于代码 bug 的问题,如果固定进入就必现崩溃的话,在测试流程就一般还是会暴露出来。当然并不完全排除代码崩溃的情况。
造成线上问题连续崩溃的,肯定是一个“变量”,那么应该是:
对于 数据库 和 存储文件 的修复,我们都做一个清理操作,以本地数据的清理,来保证 App 的正常流程。
对于重要的数据定义,可以先传入云端存起来。
这次我们出现的友盟崩溃,也正是因为读取了存在本地的问题数据而导致连续闪退的。
重新请求/运行热修复包
而对于服务端数据处理的失败,通过与服务端排查,返回正常的数据进行解决。也可以提供入口让用户上报或者直接与我们联系。
甚至考虑引入动态修复手段,解决代码 bug ,请求以及运行热修复包。
按照 微信读书团队的处理,是在 didFinishLaunching 的阶段做 hook。当触发崩溃限制数量后,进入修复,修复完成后再调用原方法 didFinishLaunching ,来按照原来的流程进入到 App。
结合我们的工程实际情况,自动修复流程与有细节差异:
需要解决这些问题,不止是对 didFinishLaunching 的阶段做 hook,还要分别对上述的情况进行处理,在崩溃数超过限制后进行拦截。
而中间整合,也发生了如服务找不到崩溃的问题等,需要一点点解决和整合。
目前临时增加一个方法:
+ (BOOL)needFixCrashes
{
NSInteger launchCrashes = [self crashCount];
if (launchCrashes >= kContinuousCrashOnLaunchNeedToReport) {
return YES;
}
return NO;
}
通过 needFixCrashes 来在各处做控制:
if(needFixCrashes){
return;
}
//原有正常逻辑处理
在修复后,我们再分别调用对应流程。相当于 [A do],[B do]..一个个去调用。
其实对于上述流程,还有一个更好的做法,限于业务时间没做。
我们可以将流程中要 hook 的对象和方法,都想办法存储起来,如使用 NSMapTable 等。
在结束修复后,再按顺序遍历出来对象和方法一个个调用,走完一套启动的流程。
这里的好处在于复用,可以直接 addObject:Selector: 的方式就增加进去,之后修复完成,不需要再写 hard code 一个个调用。
最终检测流程为:
修复的流程设计为:
实际操作当中,有不少业务待我们梳理,光做到在所有服务之前检测,如果之前没有专门的类收拢处理,就要花时间来做,或者在各处进行判断。
总的来说,最主要的思路是:
4. 总结
连续崩溃问题的发生,可以说是一个 App 最严重的问题,一般来说并不会出现。
而随着用户的增多,任何问题也有可能被无限放大。
所以作为技术人员,需要做好兜底的策略,尽量来消除此类问题,保证好用户体验,通过技术保护手段多留住一个用户就是一个用户。
何况事实证明,未雨绸缪是应该的,这次连续崩溃问题不就发生在了自己身上了吗?
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有