Android中虽然可以通过设置 Thread.setDefaultUncaughtExceptionHandler来捕获全局的所有线程的异常,但主线程抛出异常时仍旧会导致activity闪退,app进程重启。使用Cockroach后就可以保证不管怎样抛异常activity都不会闪退,app进程也不会重启。
public class CrashCatchHandler implements UncaughtExceptionHandler {
public static final String TAG = "CrashCatchHandler";
private static CrashCatchHandler crashHandler = new CrashCatchHandler();
private Context mContext;
private UncaughtExceptionHandler mDefaultCaughtExceptionHandler;
/**
* 饿汉单例模式(静态)
*
* @return
*/
public static CrashCatchHandler getInstance() {
return crashHandler;
}
/**
* 初始化
*
* @param context
*/
public void init(Context context) {
mContext = context;
//获取默认的系统异常捕获器
mDefaultCaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
//把当前的crash捕获器设置成默认的crash捕获器
Thread.setDefaultUncaughtExceptionHandler(this);
}
@Override
public void uncaughtException(Thread thread, Throwable throwable) {
if (!handleException(throwable) && mDefaultCaughtExceptionHandler != null) {
//如果用户没有处理则让系统默认的异常处理器来处理
mDefaultCaughtExceptionHandler.uncaughtException(thread, throwable);
}else {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
LogUtils.e(TAG, "error : "+ e);
}
//退出程序
AppUtil.restarteApp(mContext);
}
}
/**
* 自定义错误处理
* @param ex
* @return true:如果处理了该异常信息;否则返回false
*/
private boolean handleException(Throwable ex) {
if (ex == null) {
return false;
}
final String msg = ex.getLocalizedMessage();
if (msg == null) {
return false;
}
//使用Toast来显示异常信息
new Thread() {
@Override
public void run() {
Looper.prepare();
Toast.makeText(mContext, "很抱歉,程序出现异常!", Toast.LENGTH_LONG).show();
Looper.loop();
}
}.start();
return true;
}
}
使用很方便,在程序入口,Application类中初始化即可。
自定义Application继承自android的Application,并在Application中装载,越早初始化越好,可以在Aplication的onCreate中初始化,当然也可以根据需要在任意地方(不一定要在主线程)装载,在任意地方卸载。可以多次装载和卸载。
例如:
测试
装载Cockroach后点击view抛出异常和new Handler中抛出异常
捕获到的堆栈如下,可以看到都已经被 at com.wanjian.cockroach.Cockroach$1.run(Cockroach.java:47)
拦截,APP没有任何影响,没有闪退,也没有重启进程
可以看到 at com.wanjian.cockroach.Cockroach$1.run(Cockroach.java:47)
没有拦截,并且APP crash了。
当主线程或子线程抛出异常时都会调用exceptionHandler.handlerException(Thread thread, Throwable throwable)
exceptionHandler.handlerException可能运行在非UI线程中。
handlerException内部建议手动try{ 你的异常处理逻辑 }catch(Throwable e){ } ,以防handlerException内部再次抛出异常,导致循环调用handlerException
若设置了Thread.setDefaultUncaughtExceptionHandler则可能无法捕获子线程异常。
虽然可以捕获到所有异常,但可能会导致一些莫名其妙的问题,比如view初始化时发生了异常,异常后面的代码得不到执行,虽然不 会导致app crash但view内部已经出现了问题,运行时就会出现很奇葩的现象。再比如activity声明周期方法中抛出了异常,则生 命周期就会不完整,从而导致各种奇葩的现象。
虽然会导致各种奇葩问题发生,但可以最大程度的保证APP正常运行,很多时候我们希望主线程即使抛出异常也不影响app的正常使用,比如我们 给某个view设置背景色时,由于view是null就会导致app crash,像这种问题我们更希望即使view没法设置颜色也不要crash,这 时Cockroach就可以满足你的需求。
handlerException(final Thread thread, final Throwable throwable)内部建议请求自己服务器决定该如何处理该异常,是 直接忽略还是杀死APP又或者其他操作。
Cockroach采用android标准API编写,无依赖,足够轻量,轻量到只有不到100行代码,一般不会存在兼容性问题,也不存在性能上的问题,可以兼容所有android版本。
已上传到jcenter, compile 'com.wanjian:cockroach:0.0.5'
android中最重要的就是Handler机制了,简单来说Handler机制就是在一个死循环内部不断取走阻塞队列头部的Message,这个阻塞队列在主线程中是唯一的,当没有Message时,循环就阻塞,当一旦有Message时就立马被主线程取走并执行Message。
查看android源码可以发现在ActivityThread中main方法(main方法签名 public static void main(String[] args){}
,这个main方法是静态的,公有的,可以理解为应用的入口)最后执行了Looper.loop();
,此方法内部是个死循环(for(;;)循环),所以一般情况下主线程是不会退出的,除非抛出异常。queue.next();
就是从阻塞队列里取走头部的Message,当没有Message时主线程就会阻塞在这里,一有Message就会继续往下执行。android的view绘制,事件分发,activity启动,activity的生命周期回调等等都是一个个的Message,android会把这些Message插入到主线程中唯一的queue中,所有的消息都排队等待主线程的执行。
ActivityThread的main方法如下:
Looper.loop()
关键代码如下:
下面我们看一下Cockroach的核心代码
原理很简单,就是通过Handler往主线程的queue中添加一个Runnable,当主线程执行到该Runnable时,会进入我们的while死循环,如果while内部是空的就会导致代码卡在这里,最终导致ANR,但我们在while死循环中又调用了Looper.loop()
,这就导致主线程又开始不断的读取queue中的Message并执行,这样就可以保证以后主线程的所有异常都会从我们手动调用的Looper.loop()
处抛出,一旦抛出就会被try{}catch捕获,这样主线程就不会crash了,如果没有这个while的话那么主线程下次抛出异常时我们就又捕获不到了,这样APP就又crash了,所以我们要通过while让每次crash发生后都再次进入消息循环,while的作用仅限于每次主线程抛出异常后迫使主线程再次进入消息循环。我们可以用下面的伪代码来表示:
为什么要通过new Handler.post方式而不是直接在主线程中任意位置执行 while (true) { try { Looper.loop(); } catch (Throwable e) {} }
这是因为该方法是个死循环,若在主线程中,比如在Activity的onCreate中执行时会导致while后面的代码得不到执行,activity的生命周期也就不能完整执行,通过Handler.post方式可以保证不影响该条消息中后面的逻辑。
转自“打不死的小强,永不crash的Android”
亲测,唯不足之处是,在Activity中的生命周期方法里异常之后,crash到但未能处理Activity,导致假死状态。
下面贴出code:
Cockroach:
QuitCockroachException: