Идеальный способ установить глобальный обработчик необработанных исключений в Android

Я хочу установить глобальный обработчик необработанных исключений для всех потоков в моем приложении Android. Итак, по моему Application Подкласс Я установил реализацию Thread.UncaughtExceptionHandler как обработчик по умолчанию для неперехваченных исключений.

Thread.setDefaultUncaughtExceptionHandler(
                new DefaultExceptionHandler(this));

В моей реализации я пытаюсь отобразить AlertDialog отображение соответствующего сообщения об исключении.

Тем не менее, это не похоже на работу. Всякий раз, когда выдается исключение для любого потока, который остается необработанным, я получаю стандартный диалог ОС по умолчанию ("Извините!-Применение-остановлено-неожиданно-остановлено").

Какой правильный и идеальный способ установить обработчик по умолчанию для необработанных исключений?

5 ответов

Решение

Это должно быть все, что вам нужно сделать. (Убедитесь, что после этого процесс остановился - все может быть в неопределенном состоянии.)

Первое, что нужно проверить, это то, что обработчик Android все еще вызывается. Возможно, что ваша версия вызывается, но заканчивается фатально, и system_server показывает общий диалог, когда видит сбой процесса.

Добавьте несколько сообщений журнала в верхней части вашего обработчика, чтобы увидеть, если он туда попадает. Выведите результат из getDefaultUncaughtExceptionHandler, а затем сгенерируйте необработанное исключение, чтобы вызвать сбой. Следите за выходом logcat, чтобы увидеть, что происходит.

Я опубликовал простое решение для пользовательской обработки сбоев 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

Я думаю, чтобы отключить это в вашем методе uncaughtException() не вызывайте previousHandler.uncaughtException(), где предыдущий Handler установлен

previousHandler = Thread.getDefaultUncaughtExceptionHandler();

Я знаю, что это немного не по теме, но мы с успехом использовали бесплатный план Crittercism. Они также предлагают некоторые дополнительные функции, такие как обработка исключений, чтобы приложение не зависало.

В бесплатной версии пользователь все еще видит сбой, но по крайней мере я получаю электронную почту и трассировку стека.

Мы также используем версию для iOS (но я слышал от моих коллег, что она не так хороша).


Вот похожие вопросы:

Это не работает, пока вы не позвоните

android.os.Process.killProcess(android.os.Process.myPid());

в самом конце вашего UncaughtExceptionHandler.

Другие вопросы по тегам