iOS 启动连续闪退保护方案
引言
“如果某个实体表现出以下任何一种特性,它就具备自主性:自我修复、自我保护、自我维护、对目标的自我控制、自我改进。” —— 凯文·凯利
iOS App 有时可能遇到启动必 crash 的绝境:每次打开 App 都闪退,无法正常使用App。
为了尝试解决这个问题,微信读书开发了 iOS 连续闪退保护工具:GYBootingProtection,检测连续闪退,在连续闪退出现时,尝试自修复 App:
本文探讨了连续闪退问题的产生原因、检测、修复机制,以及如何在你的项目中引入、测试和使用GYBootingProtection。
连续闪退检测
首先要检测用户 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:
方法,然后把main()
函数中的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 loggerNSLog(@"%@", msg);}];[GYBootingProtection setReportBlock:^(NSInteger crashCounts) {// setup crash report}]; }
在
onBootingProtection
方法中添加修复逻辑,比如删除文件:- (void)onBootingProtection {// 检查 JSPatch 更新...// 删除 Documents Library Caches 目录下所有文件[GYBootingProtection deleteAllFilesUnderDocumentsLibraryCaches];... }
如需执行异步的修复逻辑,在
onBootingProtectionWithCompletion:
方法添加修复逻辑,并在完成修复后调用 completion :- (void)onBootingProtectionWithCompletion:(BoolCompletionBlock)completion {[self onBootingProtection];// 异步修复[self asyncRepairWithCompletion:^(void) {// 正常启动流程if (completion) completion();}]; }
测试
首先制造连续闪退场景:
启动后 5 秒内,双击 Home 通过上划手势 kill 掉 App,重复多次。(也可以在代码里人为制造crash)
当连续闪退超过 5 次时,会提示用户修复:
用户轻触修复,App 重置初始状态,连续闪退问题解决:
源码
https://github.com/liuslevis/GYBootingProtection
原博客地址:http://wereadteam.github.io/2016/05/23/GYBootingProtection/#rd
iOS 启动连续闪退保护方案相关推荐
- android 签名包闪退,解决APP连续闪退的方案,你还不知道的签名方式
原标题:解决APP连续闪退的方案,你还不知道的签名方式 用户在使用APP检测到连续闪退后,接下来要尝试对闪退进行修复,这里先分析可能的闪退原因: 闪退原因 连续闪退,可能是 App 启动关键路径中执行 ...
- App启动就闪退引发的深思
昨天快下班的时候,测试Q我说:"小哥哥,小哥哥,你的app怎么一启动就闪退啊,而且出现闪退后,就一直都闪退."我去,这是什么鬼,为啥我以前没发现,没办法,只能找测试妹子,问清楚具体 ...
- 全网最详细的Windows里Anaconda-Navigator启动后闪退的解决方案(图文详解)
全网最详细的Windows里Anaconda-Navigator启动后闪退的解决方案(图文详解) 参考文章: (1)全网最详细的Windows里Anaconda-Navigator启动后闪退的解决方案 ...
- Windows下,启动Redis闪退 问题解决方案
目录 前言 第1步:修改"redis.windows.conf"文件内容 第2步:输入cmd命令,启动Redis 第3步:创建Redis快速启动程序start.bat 前言 解压R ...
- 解决ubuntu上在androidstudio中启动emulator闪退的问题(1)
解决ubuntu上在androidstudio中启动emulator闪退的问题(1) 参考文章: (1)解决ubuntu上在androidstudio中启动emulator闪退的问题(1) (2)ht ...
- Genymotion 启动app闪退解决方案
Genymotion 启动app闪退解决方案 参考文章: (1)Genymotion 启动app闪退解决方案 (2)https://www.cnblogs.com/Test-road-me/p/536 ...
- android应用加固后闪退,梆梆加固,启动就闪退了
今天尝试用了梆梆加固,结果启动就闪退了. 错误信息如下,毫无头绪. 03-07 13:51:27.142 6520 6520 E CrashHandler: In thread: Thread[mai ...
- 笔记本AutoCAD启动时闪退怎么办_CAD启动就闪退的原因分析
CAD启动就闪退的原因分析 大家在使用CAD绘图时经常会遇到闪退.异常退出等情况,其实不只是在CAD中,在Word.PS以及其他软件的使用过程中都可能会遇到这样的问题.小编想提醒大家:遇到这样的情况不 ...
- Windows CMD命令行一启动就闪退问题解决
windows cmd命令行一启动就闪退,也没有什么报错信息,后来在网上找到了这篇帖子:Windows命令行cmd/PowerShell一启动就报错,解决了我的问题,遂分享一下 问题原因 Anacon ...
最新文章
- Java_中快速获取系统时间
- AI科研绘图3:排版
- 搭建kafaka_kafka单机环境搭建及其基本使用
- elasticsearch索引结构和配置优化
- ASP.NET MVC3书店--第五节 表单编辑(第二部分)(转)
- 哪个app最费电_微波炉和烤箱,买哪个划算?
- python实验结论怎么写_Python实验课:Python元组数据及其运算
- 加密货币的寒冬如何破冰?
- [转载] Java为什么不允许覆盖静态方法?
- Sqoop是一款开源的工具,主要用于在HADOOP(Hive)与传统的数据库(mysql、oracle...)间进行数据的传递...
- C/C++[codeup 1941]新版A+B
- CentOS-7 安装mosquitto(MQTT的开源消息代理)
- 联想服务器风扇智能调节,联想怎么调风扇转速
- 地图标识符号大全_资源小结:分省地图查询(9.1版)
- 深入理解Apache NIFI Run Duration
- java计算机毕业设计学生成绩管理系统源程序+mysql+系统+lw文档+远程调试
- 微信浏览器唤醒App
- python适合什么发型-下面不属于python特性的是( )。
- 设置div的内容及子标签向右对齐
- JSP概述和页面技术