Я опубликовал простое решение для пользовательской обработки сбоев Android давно. Он немного хакерский, но работает на всех версиях Android (включая Lollipop).
Сначала немного теории. Основные проблемы, возникающие при использовании обработчика необработанных исключений в Android, связаны с исключениями, генерируемыми в основном потоке (он же UI). И вот почему. Когда приложение запускает системные вызовы ActivityThread.main метод, который подготавливает и запускает Основной петлитель вашего приложения:
public static void main(String[] args) {
…
…
Looper.prepareMainLooper();
…
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
Главный петлитель отвечает за обработку сообщений, размещенных в потоке пользовательского интерфейса (включая все сообщения, связанные с отображением и взаимодействием пользовательского интерфейса). Если исключение выдается в потоке пользовательского интерфейса, оно будет перехвачено вашим обработчиком исключений, но, поскольку у вас нет метода loop()
, вы не сможете показывать пользователю какие-либо диалоги или действия, так как никого не осталось обрабатывать сообщения интерфейса пользователя для вас.
Предлагаемое решение довольно просто. Мы запускаем метод 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);
}
}
}
Как видите, обработчик необработанных исключений используется только для исключений, генерируемых в фоновых потоках. Следующий обработчик перехватывает эти исключения и передает их в поток пользовательского интерфейса:
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