iOS 启动连续闪退保护方案

版权声明:本文由刘笑江原创文章,转载请注明出处: 
文章原文链接:https://www.qcloud.com/community/article/79

来源:腾云阁 https://www.qcloud.com/community

 

一.引言

“如果某个实体表现出以下任何一种特性,它就具备自主性:自我修复、自我保护、自我维护、对目标的自我控制、自我改进。” —— 凯文·凯利

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 这类工具来检测连续闪退:

  1. 首先维护一个计数变量,表示连续闪退次数
  2. 在 PLCrashReporter 的 crash handler 中加入逻辑:如果启动 5s 内 crash 使计数器加一
  3. 每次启动时,如果连续闪退计数 > n,则检测到了连续闪退
  4. 启动后,执行一个定时任务,在 5s 后重置计数(如果 App 连续闪退则不会重置)

流程图

优缺点

通过 Mach 异常、Unix 信号、NSException 异常来检测闪退,能获得更多的 crash 上下文,但由于 crash 收集框架多使用这些方法,可能会有这样的风险:与第三方 crash 收集框架冲突导致漏检测。另外,可能会与 App 已有的异常处理代码产生耦合。

2.计时器方法

除了通过捕获异常的方式检测连续闪退,还可以通过计数器方法来检测:

  1. 维护一个计数变量,用于表示连续闪退的次数
  2. 在启动 application:didFinishLaunchingWithOptions: 后使计数加一
  3. 接着使用 dispatch_after 方法在 5s 后清零计数,如果 App 活不过 5 秒计数就不会被清零
  4. 如果发现计数变量 > n,表明 App 连续 n 次连续闪退,启动保护流程,重置计数。
  5. 当保护流程完成后,进入 App 正常启动流程

流程图

优缺点

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

综上权衡,我们使用计时器方法检测连续闪退。

三.连续闪退修复

检测到连续闪退后,接下来要尝试对闪退进行修复,这里先分析可能的闪退原因,再结合微信读书的例子说明修复流程。

1.闪退原因

连续闪退,可能是 App 启动关键路径中执行了必 crash 的代码,原因可能有:

  1. 数据库损坏:在日常使用如异常退出、断电,或者错误的操作(参考:sqlite corruption causes)。
  2. 文件损坏:处理文件时如果没有 @try...catch,损坏文件会抛出 NSException 导致 crash
  3. 网络返回数据处理异常:比如预期返回数组,但实际返回了字典,对字典对象执行 -objectAtIndex 方法会产生 crash: unknow selector send to object;,或返回破损的 Tar 包,在解压失败导致 crash。
  4. 代码 bug:当必 crash 的代码出现在启动关键路径中,就会导致连续闪退。
  5. 针对 1,可以通过工具修复数据库,或者删除 DB。针对2,可以删除文件来进行修复。对于 3 和 4,我们需要具体地分析 crash 案例,通过 JSPatch 来进行修复。

2.微信读书的修复流程

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

  1. 进入 didFinishLaunch 时检查是否有连续闪退,无则执行 5
  2. 弹 Toast 提示用户是否修复,轻触『修复』执行2,否则执行 5
  3. 尝试下载并执行 JSPatch 补丁

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

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

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

    a. 不删除文件,只修复数据库
    b. 修复前把用户数据备份到云端
    c. 收集 crash 样本,查明原因,定制 JSPatch 修复补丁并下发

  6. 退出微信读书登录状态

  7. 进入原 didFinishLaunch
    连续闪退检测 + 保护流程如图所示:

3.实现

检测和连续 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 即可。

4.使用

引入项目

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

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

    (void)onBeforeBootingProtection {
    [GYBootingProtection setLogger:^(NSString *msg) {// setup loggerNSLog(@"%@", msg); }]; [GYBootingProtection setReportBlock:^(NSInteger crashCounts) { // setup crash report }]; } 
  3. 在 onBootingProtection 方法中添加修复逻辑,比如删除文件:

    (void)onBootingProtection {
    // 检查 JSPatch 更新
    ...
    // 删除 Documents Library Caches 目录下所有文件
    [GYBootingProtection deleteAllFilesUnderDocumentsLibraryCaches];...
    }
    

    如需执行异步的修复逻辑,在 onBootingProtectionWithCompletion: 方法添加修复逻辑,并在完成修复后调用 completion :

    (void)onBootingProtectionWithCompletion:(BoolCompletionBlock)completion {[self onBootingProtection];// 异步修复[self asyncRepairWithCompletion:^(void) { // 正常启动流程 if (completion) completion(); }]; } 

5.测试

  1. 首先制造连续闪退场景:

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

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

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

源码
https://github.com/liuslevis/GYBootingProtection

 

 

转载于:https://www.cnblogs.com/purpleraintear/p/6024287.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/372108.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

实战Java内存泄漏问题分析 -- hazelcast2.0.3使用时内存泄漏 -- 2

hazelcast 提供了3中方法调用startCleanup:第一种是在ConcuurentMapManager的构造函数中,通过调用node的executorManager中的ScheduledExecutorService来创建每秒运行一次cleanup操作的线程(代码例如以下)。因为这是ConcuurentMapManager构造…

@SuppressLint(NewApi)和@TargetApi()的区别

转自:http://blog.csdn.NET/wbshuang09/article/details/44920549在Android代码中,我们有时会使用比我们在AndroidManifest中设置的android:minSdkVersion版本更高的方法,此时编译器会提示警告,解决方法是在方法上加上SuppressLin…

零基础自学编程前需要知道的知识

你是否适合编程?学习编程后能做什么?如何选择编程语言?有哪些免费的线上学习网站推荐?今天这篇好文将那些自学编程前需要了解和思考的问题都记录下来,希望能给那些刚刚开始或正准备自学编程的朋友们带去一些启发。 你是否适合自学编程 自学编程会是一个漫长而艰…

Eclipse设置黑色主题

1点击help--->install new software 2输入 http://eclipse-color-theme.github.com/update 3下载安装eclipse color theme插件如下图 4完成后点击windows--->preferences------>Appearance下多了一个Color Theme 5,点击选择喜欢的主题即可,也可以自己下载主…

wcf rest系列文章

http://www.cnblogs.com/artech/archive/2012/02/15/wcf-rest.html 需要注意的是,发布的服务,可以在web behavior中指定显示help页面。 http://localhost/ApplicationName/ServiceName.svc/help 需要注意的是,访问.svc的页面一定不要多加/;否…

登录:应用程序错误通知

几个月前,当我进行大型应用程序重构时,发现用于记录日志的基于log4j的代码确实令人讨厌,重复了数百次: if (LOG.isDebugEnabled()) {LOG.debug("Logging some stuff " stuff); }我想摆脱isXXXEnabled,这就…

如何分析线程转储–线程堆栈跟踪

本文是“ 线程转储”分析系列的第5部分。 到目前为止,您已经了解了线程的基本原理以及它们与Java EE容器和JVM的交互。 您还学习了HotSpot和IBM Java VM的不同线程转储格式。 现在是您深入分析过程的时候了。 为了使您能够从线程转储中快速识别问题模式,…

设计模式学习笔记(十三:原型模式)

1.1概述 用原型实例指定创建对象的种类,并且通过复制这些原型创建新的对象。这就是原型模式的定义。 在某些情况下,可能不希望反复使用类的构造方法创建许多对象,而是希望使用该类创建一个对象后,以该对象为原型得到该对象的若干个…

翻译的一篇关于学习编程语言的小文章

Top programming languages to get a job in Toronto in 2017 在程序开发人员和软件工程师中最容易被提及的问题之一就是:“我要学的下一门编程语言该是谁?” 我想去选一个编程语言,我希望你能给我一些关于经常使用到的编程语言的建议&#x…

从linux内核启动,学习Linux内核启动过程:从start_kernel到init

一、实验步骤:1:运行menuos:a)cd LinuxKernel/b)qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img启动后启动了MenuOS。2:使用gdb调试跟踪menuos内核启动和运行过程;a)qemu -kernel linux-3.18.6/arch/x86/bo…

linux强制回收内存,Linu系统cache强制回收

LINUX的内存管理机制,一般情况下不需要特意去释放已经使用的cache。Cache机制的存在,使得Linux对磁盘的读写速度是有较大的好处的。 在 Linux 操作系统中,当应用程序需要读取文件中的数据时,操作系统先分配一些内存,将…

Google API:如何访问Google Analytics(分析)数据?

在深入研究Google Analytics(分析)API之前,了解一些Google Analytics(分析)术语及其关系总是很有用的。 Google帐户:要访问Google Analytics(分析),用户将需要一个Google…

设计模式学习笔记(十六:桥接模式)

1.1概述 将抽象部分与它的实现部分分离,使他们都可以独立地变化。这就是桥接模式的定义。 抽象类或接口中可以定义若干个抽象方法,习惯上将抽象方法称作操作。抽象类或接口使程序的设计者忽略操作的细节,即不必考虑这些操作是如何实现的&…

linux静默删除文件夹,Linux常用命令10 - unzip

zip 是最广泛使用的归档文件, 除了linux,windows也是非常的广泛。,支持无损数据压缩。 zip 文件是包含一个或多个压缩文件或目录的数据容器。接下来,我将解释如何使用 unzip 命令通过命令行解压缩 Linux 系统中的文件。 还有与之对应就是 zip…

python基础:迭代器、生成器(yield)详细解读

1. 迭代器 迭代器是访问集合元素的一种方式。迭代器对象从集合的第一个元素开始访问,知道所有的元素被访问完结束。迭代器只能往前不会后退,不过这也没什么,因为人们很少在迭代途中往后退。 1.1 使用迭代器的优点 对于原生支持随机访问的数据…

Linux将硬盘转化为pv,Linux扩展硬盘 物理卷(PV) 卷组(VG) 逻辑卷(LV)

1、给虚拟机添加两块新的sata虚拟硬盘,容量8G和10G# fdisk -l 命令2、分别在这两个硬盘上建立pvPvcreate /dev/sdb 创建一个物理卷/dev/sdb 磁盘名是 fdisk -l 查询出来的Pvscan 查看当前所有物理卷Pvdisplay 查看当前所有物理卷的详情3、创建VG,使得…

ubuntu 16.10 shu rufa meiy ou l e geng xi zhi hou

转载于:https://www.cnblogs.com/ganmk--jy/p/6035894.html

红旗linux 进不去图形界面,进不了红旗Linux6.0的图形界面请高手帮忙

习生 于 2008-11-02 11:08:42发表:引用:原帖由 zhaoruiqi 于 2008-11-2 10:03 发表 我的也是进不了图形界面,用文本安装后进系统也一样正常按rtl的方法对xorg.conf进行修改,已经能进入图形界面。你看看楼上rtl的回复的能否对你有帮助。zhaoruiqi 于 2008-11-02 10:0…

基于Activiti工作流引擎实现的请假审核流程

概要 本文档介绍的是某商用中集成的Activiti工作流的部署及使用,该框架用的Activiti版本为5.19.0。本文档中主要以一个请假流程为例子进行说明,该例子的流程图如下: 这是一个可以正常运作的工作流业务了,但是它也有不足的地方&…

linux编译ffmpeg成so,「ffmpeg」一 mac 环境下编译ffmpeg,生成so库文件

1.下载ffmpeg源码,官网,我这里直接采用git 方式下载:下载ffmpeg.png终端输入git命令:静静等待~最后下载的版本为3.4.6 。image.png这里注意一下,刚开始我用的ndk版本是ndk-17b,在编译该版本的ffmpeg时始终失败&#xf…