有赞 Android 崩溃保护的探究及实践
概述
Android 的 Crash 是件让人头疼的事,测试阶段好好的代码一上线就各种崩溃,即便是一个微不足道的 bug 也得发个 hotfix。很多时候我们更希望即便个别功能没法使用也不要崩溃,比方点击图片想看大图时,因为 onClick 回调中没做判空解决等导致 APP 崩溃了,这时我们更希望即便不能看大图也不要崩溃,这时你可以考虑使用?Bandage?,当然?Bandage?的强大之处远不止这些。
Bandage是什么
Bandage?:绷带,通用的止血工具。?Bandage?可以最大程度保证 APP 可用,任何 Java 异常都不会导致 APP 崩溃。?Bandage?试图在 APP 即将崩溃时尽量去挽救,不至于情况更糟糕?(医生,我觉得我还可以再抢救一下)?。当然有些异常是肯定要终止 APP 的,不然可能会给公司造成更大的损失,对于这种异常,可以通过黑白名单决定要不要终止 APP。
Bandage 是如何实现的
阻拦 Activity 生命周期的异常
Activity 生命周期(比方?onCreate?,?onResume?等)抛出异常时,假如不?finish?掉抛出异常的 Activity 的话会导致黑屏。
如何阻拦?
简单来说是替换了?ActivityThread.mH.mCallback
Activity 生命周期所有方法都是在?mH?的?handleMessage?方法中调用的,只需能阻拦这个?handleMessage?方法就能阻拦所有生命周期的异常。然而我们没法通过反射替换掉这个?mH?对象。由于?mH?是 ActivityThread 中一个 H 类的实例,H 类又继承自?Handler?,H 类又是 ActivityThread 中的一个私有类,但是?Handler?会在调用?handleMessage?前调用?mCallback.handleMessage?,?mCallback?是可以被替换掉的。
替换方式如下,可以参考?hookmH 方法
相关代码
Looper.loop()?方法
Android 主线程所有的消息都是在这调用的,包括生命周期回调,view 绘制,自己 new Handler post 的消息等等。?msg.target?就是相关联的 Handler,假如自己 new Handler 并 post 消息的话那么这个 target 就是你 new 的 Handler,也就是说哪个 Handler post 的 Message 就交给那个 Handler 解决。生命周期相关的 Message 是?mH?post的,所以要交给?mH?解决。
Handler.dispatchMessage?方法
可以看到会先判断?mCallback?能否存在,存在的话就交给?mCallback?的?handleMessage?解决,假如?mCallback?的?handleMessage?返回 true 则不再调用 Handler 的?handleMessage?方法。所以我们可以通过上述方式实现阻拦 Activity 生命周期的异常。
finish 生命周期异常的 Activity
通过?ActivityManager?的?finishActivity?结束掉生命周期抛出异常的 Activity。
各版本 Android 的 ActivityManager 获取方式,?finishActivity?的参数,?mToken(binder对象)?的获取不一样,我们可以去每个版本的 Activity 的 finish 方法中查看,比方 API26 调用的是如下方法:
mToken?可以从?mH?的 message 中获取。具体实现可以参考这?finish Activity?。
阻拦主线程的其余异常
上文说过 Android 主线程所有的消息都是在?Looper.loop()?方法中调用的,只需能 try catch 住这个 loop 方法就能实现阻拦主线程的所有异常,我们可以在?uncaughtException?方法中执行如下代码。实现方式如下:
为什么要加个 while 死循环?
假如不加 while 的话就只能捕获一次主线程的异常,下次主线程再抛出异常的话就没法在这捕获了。
加了 while 不会 ANR 吗?
不会的,由于 while 内部又调用了?Looper.loop()?,这时主线程就又开始消息循环了,主线程会不断的取走主线程中唯一的消息队列头部的消息执行掉,而后等待下一个消息的到来。所以主线程不会卡住,当然不会 ANR。每次主线程抛出异常时就会被我们的 try catch 捕获到,而后又进入了 while 循环。
阻拦其余异常
通过 Thread.setDefaultUncaughtExceptionHandler 捕获其余异常。
异常解决
有些异常是肯定要终止 APP 的,不然可能会对公司造成更大的损失,而有些异常是可以直接忽略的。建议通过黑白名单控制阻拦到的异常是直接忽略还是杀进程。可以在 APP 启动时,或者者 crash 后下次重启时请求接口升级黑白名单。
什么样的异常可以不杀进程?
假如忽略该异常不会对公司造成损失可以不杀进程
假如忽略该异常只是造成某个 Activity 打不开,而没有其余反作用的话可以不杀进程
假如忽略该异常只是部分 UI 不展现,而没有其余反作用的话可以不杀进程
单纯的 UI 展现 Activity 的话可以不杀进程(比方只是展现商品介绍等),涉及到金钱的 Activity 建议杀进程(比方当前 Activity 中有些开单计算,支付,退款等逻辑)
对于少量顽疾,每个版本都出现,但又找不到问题所在,忽略后又没啥影响的异常可以不杀进程
总之,要不要杀进程由你决定,只需可以提升客户体验,并且不会对公司造成额外损失都可以不杀进程。
注意:?ViewRootImpl?抛出异常时可能会导致黑屏,这种情况建议直接终止 APP。
遍历出错堆栈,假如是?ViewRootImpl?相关的异常建议直接杀进程,不然可能导致黑屏。
黑白名单如何配置?
只根据异常堆栈的话可能无法唯一确定一个问题,比方有两个 Activity,各有一个 Handler,都 post 了一个 Runnable,run 方法中一开始就都抛出了空指针异常,假如单纯根据异常堆栈的话我们无法确定究竟是哪个 Activity 中的 Handler 抛出的异常。可以根据当前所在的 Activity 和异常堆栈来处理。假如还是无法确定问题出处的话,谨慎起见建议全部终止 APP。不过绝大多数情况下只根据异常堆栈即可以确定问题出处。
为了减少获取黑白名单的数据量,可以把当前所在 Activity 的类名称和异常堆栈拼接在一起,而后计算 md5 值,黑白名单中只包含该 md5 值就可,用户端捕获到异常时只要要进行同样的计算逻辑并判断md5值能否包含在黑白名单里。
Bandage 的不足之处
Bandage?很多情况下只是忽略掉异常,让主线程再次进入消息循环,执行下一个消息,?Bandage?完全不清楚应该如何挽救。所以有时你会发现 Activity 根本打不开,又或者者 Activity 中部分数据显示不完整,又或者者 view 点击没反应,又或者者其余奇奇怪怪的问题。但有些情况下直接忽略掉某些崩溃是没有任何影响的,或者许直接忽略是最明智的选择。
Bandage?可以最大程度保证 APP 可用,有人说这种阻拦方式很暴力,但 Android 默认的异常杀进程逻辑不是更暴力吗,杀进程并不能处理问题,杀进程后再自动恢复 Activity 反而会导致更多的问题。
bugly 使用问题
bugly 也会通过设置 Thread.setDefaultUncaughtExceptionHandler 监听应用的异常,监听到后只是上报一下,而后又交给了原来的异常解决器解决,?Bandage?也会设置 DefaultUncaughtExceptionHandler,所以为了能让 bugly 主动上报异常,建议在 bugly 初始化前初始化?Bandage?。另外 Activity 生命周期的异常会被?Bandage?捕获,所以不会自动上传到 bugly,可以手动上传,同理 looper.loop() 因为被?Bandage?捕获了,所以也不会自动上传到 bugly。特别注意:?Bandage?所捕获到的异常可能是因为上一个异常被忽略导致的,对于这种异常我们只要要修复之前的异常即可以了。
一点建议
开发阶段可以不启用?Bandage?,以免发现不了 bug,假如开发阶段肯定要启用?Bandage话可以在捕获到异常时开启个警告 Activity,或者者所有 Activity 顶部置为绿色等,用于提醒开发者已经出现了 bug,这时可以直接手动杀进程查 bug 了。
效果图
码链接
android-notes/Cockroach/tree/X
关注+点赞,私信关键词 【资料】就可获取更多有关安卓开发的资料(面试资源与经验总结,BAT内推,高级UI、性能优化、架构师课程、NDK、混合式开发全方 面的 Android高级实践技术讲解以及在线答疑等。)!
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » 有赞 Android 崩溃保护的探究及实践