解决APP连续闪退的方案

首先要检测用户 App 连续闪检测方法可以捕获异常和计时器。

1. 捕获异常

可通过捕获异常来检测连续闪退,异常有以下几种:

Mach 异常:EXC_CRASH

UNIX 信号:SIGABRT

NSException 异常:通过 NSUncaughtExceptionHandler 捕获

念茜漫谈 iOS Crash 收集框架详细介绍了 Mach 异常和 Unix 信号捕获 crash 机制。简单来说,异常一般来自 iOS 微内核 Mach,然后在 BSD 层转化为 UNIX SIGABRT 信号,标准 POSIX 向用户提供信号形式。NSException 用户在处理 App 逻辑时,用编程方法抛出。

如何捕捉异常

以下方法捕获异常:

利用 Mach API 捕获 Mach 异常

通过 POSIX API 注册 signal(SIGSEGV,signalHandler) 来捕获 UNIX 异常信号

注册 NSUncaughtExceptionHandler 捕获应用级异常

Crash 上报工具如 PLCrashReporter 注册 Mach UNIX信号 的 handler 达到检测目的,为用户提供异常接口。

如何检测

可以利用 PLCrashReporter 检测连续闪退的工具:

首先,维护计数变量,表示连续闪回次数

在 PLCrashReporter 的 crash handler 添加逻辑:如果启动 5s 内 crash 加一个计数器

每次启动时,如果连续闪回计数 > n,连续闪回被检测到

启动后,在 5s 后重置计数(如果 App 连续闪退不会重置。

流程图

优缺点

通过 Mach 异常、Unix 信号、NSException 异常检测闪退可获得更多 crash 上下文,但因为 crash 这些方法经常用于收集框架,可能存在与第三方 相比的风险crash 收集框架冲突导致泄漏检测。此外,可能与 有关App 现有的异常处理代码产生耦合。

2. 计时器方法

除捕获异常外,还可通过计数器检测连续闪退:

用来表示连续闪回次数的计数变量

在启动 application:didFinishLaunchingWithOptions: 后用计数加一

接着使用 dispatch_after 方法在 5s 后清零计数,如果 App但是 5 秒计数不会被清除

若发现计数变量 > n,表明 App 连续 n 连续闪回,启动保护过程,重置计数。

保护过程完成后,进入 App 正常启动过程

流程图

优缺点

而且计数器方法逻辑简单,与原代码耦合小。虽然可能有误报(启动后立即 kill 掉,误认为 crash),但误报率可以通过设置阈值来降低。

综上所述,我们使用计时器检测连续闪退。

连续闪退修复

检测到连续闪退后,试着修复闪退。在这里,首先分析可能的闪退原因,然后用微信阅读的例子解释修复过程。

闪退原因

持续闪回,可能是 App 必 在启动关键路径时执行crash 代码的原因可能是:

数据库损坏:在日常使用中,如异常退出、断电或错误操作(见:sqlite corruption causes)。

文件损坏:如果没有文件处理@try...catch,损坏的文件将被抛出NSException导致 crash

网络返回数据处理异常:如预期返回数组,但实际返回字典,执行字典对象-objectAtIndex方 ** 产生crash: unknow selector send to object;,或返回损坏的 Tar解压失败导致 包crash。

代码 bug:当必 crash 代码出现在启动关键路径中,会导致连续闪回。

1可以通过工具修复数据库或删除 DB。对于2,可以删除文件进行修复。对于 3 和 4,我们需要分析 crash 案例,通过 JSPatch 来进行修复。

微信阅读修复流程

为了应对上述导致连续闪退的原因,微信读书的修复流程为:

进入 didFinishLaunch 检查是否有连续闪回,否则将实施 5

弹 Toast 提示用户是否修复,轻触『修复』执行2,否则执行 5

尝试下载和执行 JSPatch 补丁

这里是为了解决上述第4点 - 代码 bug使用 引起的闪回JSPatch[github]可热修复。didFinishLaunching 时,会卡住界面,要求检查是否有可用 JSPatch 脚本,如果有,加载执行,解决代码 bug 导致闪回。

尝试删除Documents/Library/Caches目录下的所有文件

所有用户数据都直接在这里删除,适合微信阅读。所有数据都在云中,删除后可以完全从云中恢复。如果你的 App 不属于这种场景,应该在 repairBlock 中自定义修复逻辑,比如:

a. 不删除文件,只修复数据库

b. 修复前将用户数据备份到云端

c. 收集 crash 样品,找出原因,定制 JSPatch 修复补丁并发

退出微信阅读登录状态

进入原 didFinishLaunch

连续闪退检测的保护过程如图所示:

实现

检测和连续 crash 修复需要修改原件-application:didFinishLaunchingWithOptions:逻辑有几种方法:

直接修改-application:didFinishLaunchingWithOptions:方法。

新建一个SubAppDelegate类来继承AppDelegate,覆盖-application:didFinishLaunchingWithOptions:方法,然后 ** in()函数中的AppDelegate替换为SubAppDelegate

新建一个AppDelegate扩展,然后使用 method swizzle 替换方法-application:didFinishLaunchingWithOptions:方法。

以上三种方案对现有项目的变更成本为 1 > 2 > 3。因此,我们使用 3 代替源代码修改成本最低的方案-application:didFinishLaunchingWithOptions:。

检测逻辑 GYBootingProtection 已经处理好了,修复处理预留了界面,用户可以自定义,将自定义的修复过程引入 repairBlock 即可。

使用

引入项目

下载(github)源码 ,将src所有文件在目录下拖到你 Xcode 项目

在AppDelegate GYBootingProtection.m的onBeforeBootingProtection添加检测前需要执行的代码,如设置crash上报:

- (void)onBeforeBootingProtection {

[GYBootingProtection setLogger:^(NSString*msg) {

// setup logger

NSLog(@"%@",msg);

}];

[GYBootingProtection setReportBlock:^(NSIntegercrashCounts) {

// setup crash report

}];

}

在onBootingProtection在方法中添加修复逻辑,如删除文件:

- (void)onBootingProtection {

//检查 JSPatch 更新

...

// 删除 Documents Library Caches 目录下所有文件

[GYBootingProtection deleteAllFilesUnderDocumentsLibraryCaches];

...}

如果需要执行异步修复逻辑,则在onBootingProtectionWithCompletion:方法添加修复逻辑,修复后调用 completion :

- (void)onBootingProtectionWithCompletion:(BoolCompletionBlock)completion {

[selfonBootingProtection];

// 异步修复

[selfasyncRepairWithCompletion:^(void) {

// 正常启动过程

if(completion) completion();

}];

}

测试

首先,制作连续闪退场景:

启动后5 秒内,双击 Home 通过上划手势 kill 掉 App,重复多次(也可以在代码中人工制作。crash)

当连续闪回超过 5 次时,会提示用户修复:

用户轻触修复,App 重置初始状态,连续闪退问题解决!

扫码免费用

源码支持二开

申请免费使用

在线咨询