目录
(1)主线程或子线程抛出异常后,迫使主线程Looper持续loop()
(3)当绘制、测量、布局出现问题导致Crash时,关闭异常界面。
前言
Crash率是衡量一个App好坏的重要指标之一。如果你忽略了它的存在,它就会得寸进尺,愈演愈烈,最后造成大量用户的流失,进而给公司带来无法估量的损失。
上一篇(Android 性能优化(三)认识异常Exception和错误Error)讲到造成Crash的原因却有很多,比如:运行时异常的空指针、数组越界、未实例化、强制类型、低内存机制(Android 性能优化(五)Crash治理之LMK,内存泄漏及OOM检测)等等,有些时候我们在开发测试阶段都没有出现异常崩溃现象,而发布上线后到了用户手机就会出现各种奇怪闪退。所以,我们要去努力实现一个永远打不死的小强 —— 不会出现Crash闪退的APP。
Githup开源地址:
https://github.com/aiyangtianci/AndroidCrashX
一、治理原则
当我们遇见一个bug时,不能依赖于拦截异常,然后改一行代码就行了,而是学习《美团外卖Android Crash治理之路》说的“预防胜于治理”。对于Crash的治理,我们尽量遵守以下三点原则:
1、异常不能随便吃掉。
随意的使用try-catch,只会增加业务的分支和隐蔽真正的问题,要了解Crash的本质原因,根据本质原因去解决。catch的分支,更要根据业务场景去兜底,保证后续的流程正常。
2、由点到面。
一个Crash发生了,我们不能只针对这个Crash的去解决,而要去考虑这一类Crash怎么去解决和预防。只有这样才能使得这一类Crash真正被解决。
3、预防胜于治理。
当Crash发生的时候,损失已经造成了,我们再怎么治理也只是减少损失。尽可能的提前预防Crash的发生,可以将Crash消灭在萌芽阶段。
二、治理实践
由于开发人员编写代码不小心而导致的Crash,常见的Crash类型包括:空节点、角标越界、类型转换异常、实体对象没有序列化、数字转换异常、Activity或Service找不到等。这类Crash是App中最为常见的Crash,也是最容易反复出现的。解决这类Crash需要由点到面,根据Crash引发的原因和业务本身,统一集中解决。在获取Crash堆栈信息后,解决这类Crash一般比较简单,更多考虑的应该是如何避免。下面介绍两个我们治理的量比较大的Crash。
(1)NullPointerException
造成这种Crash一般有两种情况:
1、对象本身没有进行初始化或者手动置为null了,然后对其进行操作;
治理方法:
- 对可能为空的对象做判空处理或加try-catch保护。
不要吞掉异常,若为空,对其再次进行初始化。
- 使用@NonNull和@Nullable注解。
标注在方法、字段、参数上,表示对应的值不可以为空或值可以为空,否则IDE会警告。
- 考虑使用Kotlin语言。代码简洁、类型检测、类型转换等。
// 加?表示变量的值可以为null ,否则提示错误❎
var age: String? = "23"
// !!表示抛出空指针异常❌
val ageInt = age!!.toInt()
2、对象已经初始化后,但被虚拟机GC回收,然后对其进行操作。
治理方法:
这种情况大部分是由于Activity销毁或Fragment被移除后,在Message、Runnable、http请求等回调中执行了一些代码导致的。可以将Message、Runnable回调时,判断Activity/Fragment是否销毁或被移除;加try-catch保护;在BaseActivity、BaseFragment的onDestory()里把当前Activity所发的所有请求取消掉。