Я давно опублікував просте рішення для власної роботи з аваріями 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();
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