很久以前,我发布了用于自定义处理 Android 崩溃的简单解决方案。这有点 hacky,但它适用于所有 Android 版本(包括 Lollipop)。
首先是一点理论。在 Android 中使用未捕获的异常处理程序时的主要问题是在主(又名 UI)线程中抛出的异常。这就是为什么。当应用程序启动时,系统调用ActivityThread.main方法准备并启动应用程序的Main looper:
public static void main(String[] args) {
…
…
Looper.prepareMainLooper();
…
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
Main Looper 负责处理发布在 UI 线程中的消息(包括所有与 UI 渲染和交互相关的消息)。如果在 UI 线程中抛出异常,它将被您的异常处理程序捕获,但由于您已用尽loop()
方法,您将无法向用户显示任何对话框或活动,因为没有人可以处理 UI 消息为你。
建议的解决方案非常简单。我们自己运行Looper.loop
方法并用 try-catch 块包围它。当捕获到异常时,我们会按需要处理它(例如启动我们的自定义报告活动)并Looper.loop
再次调用方法。
以下方法演示了这种技术(它应该从Application.onCreate
侦听器中调用):
private void startCatcher() {
UncaughtExceptionHandler systemUncaughtHandler = Thread.getDefaultUncaughtExceptionHandler();
// the following handler is used to catch exceptions thrown in background threads
Thread.setDefaultUncaughtExceptionHandler(new UncaughtHandler(new Handler()));
while (true) {
try {
Looper.loop();
Thread.setDefaultUncaughtExceptionHandler(systemUncaughtHandler);
throw new RuntimeException("Main thread loop unexpectedly exited");
} catch (Throwable e) {
showCrashDisplayActivity(e);
}
}
}
如您所见,未捕获的异常处理程序仅用于后台线程中抛出的异常。以下处理程序捕获这些异常并将它们传播到 UI 线程:
static class UncaughtHandler implements UncaughtExceptionHandler {
private final Handler mHandler;
UncaughtHandler(Handler handler) {
mHandler = handler;
}
public void uncaughtException(Thread thread, final Throwable e) {
mHandler.post(new Runnable() {
public void run() {
throw new BackgroundException(e);
}
});
}
}
我的 GitHub 存储库中提供了一个使用此技术的示例项目:https ://github.com/idolon-github/android-crash-catcher