Ідеальний спосіб встановити глобальний незахоплений обробник винятків в Android


88

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

Thread.setDefaultUncaughtExceptionHandler(
                new DefaultExceptionHandler(this));

У своїй реалізації я намагаюся відобразити AlertDialogвідповідне повідомлення про виняток.

Однак це, здається, не працює. Кожного разу, коли для будь-якого потоку, який не обробляється, виникає виняток, я отримую діалогове вікно з типовим запасом ("Вибачте! -Додаток-зупинився-несподівано").

Який правильний та ідеальний спосіб встановити обробник за замовчуванням для незахоплених винятків?


1
Чи можете ви поділитися кодом того самого ...
Code_Life

2
Якщо ви хочете , щоб увійти ваші виключення, подивіться на acra.ch . ACRA дозволяє надсилати звіти про помилки в Google-Doc або вам по електронній пошті.
Олександр Пача

1
@Alexander Або ви можете просто використовувати Google Analytics для Android і реєструвати всі винятки, які хочете ...
IgorGanapolsky

Це може бути допомога stackoverflow.com/questions/19897628/…
Арісто Майкл

Відповіді:


24

Це має бути все, що вам потрібно зробити. (Переконайтеся, що після цього процес зупиняється - все може бути в невизначеному стані.)

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

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


10
"викидання невпійманого винятку, щоб викликати збій після усунення помилки", як і раніше важливо. Я це просто пережив. Мій додаток заблоковано після того, як я обробив виняток і не видав незахоплений виняток.
OneWorld

@OneWorld Jepp те саме тут - принаймні для блокуючої частини - здається, немає способу "врятувати" програму від збою все-таки.
AgentKnopf

@Zainodis Я опублікував на це запитання злегка нетематичну відповідь, даючи посилання на Crittercism - я думаю, у них є функція, яка дозволяє "врятувати" програму від збою в кінці кінців. Не впевнений - я використовую лише безкоштовну банкоматну версію.
Richard Le Mesurier

@RichardLeMesurier Дякую за підказку - перевірю :)!
AgentKnopf

Дивно !!! uncaughtexception викликається, навіть якщо я обробляю виключення в своїй активності. Будь-яка ідея.
Hiren Dabhi

12

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


Як можна здогадатися, таким чином можна не тільки показати власне діалогове вікно помилок, але й дозволити користувачеві ігнорувати виняток і продовжувати роботу з додатком (і хоча це видається поганою ідеєю для опублікованих програм, це може бути досить зручно під час налагодження або тестування).
Ідолон

Привіт, хоча це спрацьовує при лові невпійманих винятків, але з якихось причин цей код завжди створює винятки. Спочатку, хоча це було через рядок RuntimeException, який у вас є в методі startCatcher, але я все ще отримую виняток після його видалення. Я не впевнений, що це потрібна річ. У будь-якому разі виняток, який я отримую, - це java.lang.RuntimeException: Performing pause of activity that is not resumed. Я вважаю, що коли я намагаюся запустити власний цикл, система призупиняє діяльність, яка ось-ось почнеться? Я не впевнений, але будь-яка допомога буде вдячна. Дякую
sttaq

також якщо я видаляю startCatcher, тобто запускаю програму без вашого коду, тоді все працює нормально без будь-яких винятків.
sttaq

@sttaq Яку версію Android ви використовуєте?
Ідолон

Думаю, я намагався це зробити 2.3.x
sttaq

3

Я думаю, щоб вимкнути те, що у вашому методі uncaughtException () не викликати previousHandler.uncaughtException (), де previousHandler встановлений

previousHandler = Thread.getDefaultUncaughtExceptionHandler();

2

FWIW Я знаю, що це трохи не тематично, але ми з успіхом використовуємо безкоштовний план Crittercism . Вони також пропонують деякі преміум-функції, такі як обробка винятків, щоб програма не аварійно працювала.

У безкоштовній версії користувач все ще бачить збій, але принаймні я отримую електронну пошту та трасування стека.

Ми також використовуємо версію iOS (але я чув від своїх колег, що це не зовсім добре).


Ось подібні запитання:


1

Це не працює, поки ви не зателефонуєте

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

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.