Thread.UncaughtExceptionHandler
- 对于未try catch的异常,会回调到接口
Thread.UncaughtExceptionHandler
的uncaughtException(Thread t, Throwable e)
方法。
uncaughtException()
方法执行完后线程会阻塞住,因此系统默认实现是直接杀进程。系统默认实现的是com.android.internal.os.RuntimeInit
中的KillApplicationHandler
。我们可以看到在finally代码块中调用了Process.killProcess()
然后退出程序。
private static class KillApplicationHandler implements Thread.UncaughtExceptionHandler {
@Override
public void uncaughtException(Thread t, Throwable e) {
try {
ensureLogging(t, e);
if (mCrashing) return;
mCrashing = true;
if (ActivityThread.currentActivityThread() != null) {
ActivityThread.currentActivityThread().stopProfiling();
}
ActivityManager.getService().handleApplicationCrash(
mApplicationObject, new ApplicationErrorReport.ParcelableCrashInfo(e));
} catch (Throwable t2) {
if (t2 instanceof DeadObjectException) {
} else {
try {
Clog_e(TAG, "Error reporting crash", t2);
} catch (Throwable t3) {
}
}
} finally {
Process.killProcess(Process.myPid());
System.exit(10);
}
}
}
复制代码
- 可以通过调用
Thread.setUncaughtExceptionHandler()
给指定线程设置自定义UncaughtExceptionHandler
。或者调用Thread.setDefaultUncaughtExceptionHandler()
静态方法来给所有线程设置自定义UncaughtExceptionHandler
,但这会导致无法通过调用Thread.getDefaultUncaughtExceptionHandler()
来获取到系统的KillApplicationHandler
。当然我们可以先在set
之前进行缓存。
接管Looper
- 我们可以自定义
UncaughtExceptionHandler
,在uncaughtException()
方法中try catch住线程的Looper.loop()
。如果是子线程要确保已经设置Looper。
public class MyCaughtExceptionHandler implements Thread.UncaughtExceptionHandler {
@Override
public void uncaughtException(@NonNull Thread t, @NonNull Throwable e) {
if (handleException(t, e)){
if (t.getName().equals("main")) {
for (;;) {
try {
Looper.loop();
} catch (Exception e1) {
handleException(t, e1);
}
}
}
}
}
private boolean handleException(Thread t, Throwable e){
if (e.getClass().getSimpleName().equals("NullPointerException")) {
Log.e("?????", "NullPointerException: ");
return true;
} else {
e.printStackTrace();
Thread.UncaughtExceptionHandler uncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
if (uncaughtExceptionHandler != null){
uncaughtExceptionHandler.uncaughtException(t, e);
}
return false;
}
}
}
复制代码
- 在主线程中通过死循环调用
Looper.loop()
,使得MessageQueue中之后的消息都会在这里处理。并对Looper.loop()
进行try catch。当遇到异常的时候判断是否能处理异常,如果能就不会Crash,程序继续运行。如果不能处理我们可以再交回给KillApplicationHandler
。
- 我们可以通过云端下发需要保护的具体Crash。比如:包含进程名,Android版本,App版本,方法名,异常全类名等。当catch住的异常能在云端下发的保护Crash名单中匹配到,则进行上报后不做处理。让程序继续正常运行。
- 我们可以通过这种方式,对一些厂商的定制系统的兼容性问题、Android系统Bug、线上的灰度问题等都可以采用这个方法进行兜底。方案改动小,业务侧完全无感知,后期维护成本低。