Idealny sposób na ustawienie globalnej obsługi nieprzechwyconych wyjątków w systemie Android

88

Chcę ustawić globalną procedurę obsługi nieprzechwyconych wyjątków dla wszystkich wątków w mojej aplikacji na Androida. Tak więc w mojej Applicationpodklasie ustawiłem implementację Thread.UncaughtExceptionHandlerjako domyślną obsługę nieprzechwyconych wyjątków.

Thread.setDefaultUncaughtExceptionHandler(
                new DefaultExceptionHandler(this));

W mojej implementacji staram się AlertDialogwyświetlić odpowiedni komunikat wyjątku.

Jednak to nie działa. Zawsze, gdy wyjątek jest zgłaszany dla dowolnego wątku, który nie jest obsługiwany, pojawia się standardowe okno dialogowe domyślnego systemu operacyjnego („Przepraszamy!

Jaki jest prawidłowy i idealny sposób ustawienia domyślnej procedury obsługi nieprzechwyconych wyjątków?

Samuh
źródło
1
Czy możesz udostępnić kod dla tego samego ...
Code_Life
2
Jeśli chcesz rejestrować wyjątki, rzucić okiem na acra.ch . ACRA umożliwia wysyłanie raportów o błędach do Google-Doc lub do Ciebie za pośrednictwem poczty e-mail.
Alexander Pacha,
1
@Alexander Lub możesz po prostu użyć Google Analytics dla Androida i zarejestrować wszystkie wyjątki, które chcesz ...
IgorGanapolsky
To może być pomoc stackoverflow.com/questions/19897628/…
Aristo Michael

Odpowiedzi:

24

To wszystko, co musisz zrobić. (Upewnij się, że później zatrzymasz proces - stan może być niepewny).

Pierwszą rzeczą do sprawdzenia jest, czy nadal jest wywoływany program obsługi Androida. Możliwe, że Twoja wersja jest wywoływana, ale kończy się niepowodzeniem, a serwer_systemowy wyświetla ogólne okno dialogowe, gdy zauważy awarię procesu.

Dodaj komunikaty dziennika na górze programu obsługi, aby sprawdzić, czy się tam dostanie. Wydrukuj wynik z getDefaultUncaughtExceptionHandler, a następnie wyślij nieprzechwycony wyjątek, aby spowodować awarię. Obserwuj wyjście logcat, aby zobaczyć, co się dzieje.

zanikać
źródło
10
„rzucanie nieprzechwyconego wyjątku, aby spowodować awarię po obsłużeniu błędu” jest nadal ważne. Po prostu tego doświadczyłem. Moja aplikacja została zablokowana po obsłużeniu wyjątku i nie zgłosiłem nieprzechwyconego wyjątku.
OneWorld
@OneWorld Jepp to samo tutaj - przynajmniej w przypadku części blokującej - w końcu wydaje się, że nie ma sposobu, aby „uratować” aplikację przed awarią.
AgentKnopf
@Zainodis Opublikowałem nieco niezwiązaną z tematem odpowiedź na to pytanie, podając link do Crittercism - myślę, że mają one funkcję, która w końcu pozwala "uratować" aplikację przed awarią. Nie jestem pewien - korzystam tylko z darmowej wersji atm.
Richard Le Mesurier
@RichardLeMesurier Dzięki za podpowiedź - sprawdzę :)!
AgentKnopf
Dziwne!!! uncaughtexception o nazwie nawet jeśli obsługuję wyjątek w mojej aktywności. Dowolny pomysł.
Hiren Dabhi
12

Już dawno opublikowałem proste rozwiązanie do niestandardowej obsługi awarii Androida. Jest trochę hacky, ale działa na wszystkich wersjach Androida (w tym Lollipop).

Najpierw trochę teorii. Główne problemy występujące podczas korzystania z modułu obsługi nieprzechwyconych wyjątków w systemie Android obejmują wyjątki zgłaszane w głównym wątku (znanym również jako UI). A oto dlaczego. Gdy aplikacja uruchamia się, system wywołuje metodę ActivityThread.main , która przygotowuje i uruchamia główny pętlę Twojej aplikacji:

public static void main(String[] args) {
  …
  …
    Looper.prepareMainLooper();
  …
    Looper.loop();
    throw new RuntimeException("Main thread loop unexpectedly exited");
}

Główny looper jest odpowiedzialny za przetwarzanie komunikatów publikowanych w wątku UI (w tym wszystkich komunikatów związanych z renderowaniem i interakcją UI). Jeśli wyjątek zostanie zgłoszony w wątku interfejsu użytkownika, zostanie on przechwycony przez moduł obsługi wyjątków, ale ponieważ nie masz loop()metody, nie będziesz w stanie pokazać użytkownikowi żadnego okna dialogowego ani działania, ponieważ nie ma już nikogo do przetwarzania komunikatów interfejsu użytkownika dla Was.

Zaproponowane rozwiązanie jest dość proste. Uruchamiamy Looper.loopmetodę samodzielnie i otaczamy ją blokiem try-catch. Kiedy wyjątek zostanie przechwycony, przetwarzamy go tak, jak chcemy (na przykład rozpoczynamy naszą niestandardową aktywność raportu) i Looper.loopponownie wywołujemy metodę.

Poniższa metoda demonstruje tę technikę (należy ją wywołać ze Application.onCreatesłuchacza):

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);
        }
    }
}

Jak widać, procedura obsługi nieprzechwyconych wyjątków jest używana tylko dla wyjątków zgłaszanych w wątkach w tle. Następująca procedura obsługi przechwytuje te wyjątki i propaguje je do wątku interfejsu użytkownika:

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);
            }
        });
    }
}

Przykładowy projekt wykorzystujący tę technikę jest dostępny na moim repozytorium GitHub: https://github.com/idolon-github/android-crash-catcher

Idolon
źródło
Jak można się domyślić, w ten sposób można nie tylko wyświetlić niestandardowe okno dialogowe błędu, ale także pozwolić użytkownikowi zignorować wyjątek i kontynuować pracę z aplikacją (i chociaż wygląda to na zły pomysł dla opublikowanych aplikacji, może to być całkiem wygodne podczas debugowania lub sesji testowej).
Idolon
Cześć, chociaż to działa w wyłapywaniu nieprzechwyconych wyjątków, ale z jakiegoś powodu ten kod zawsze generuje wyjątki. Początkowo myślałem, że to z powodu linii RuntimeException, którą masz w metodzie startCatcher, ale po usunięciu nadal otrzymuję wyjątek. Nie jestem pewien, czy jest to wymagane. W każdym razie wyjątek, który dostaję, to java.lang.RuntimeException: Performing pause of activity that is not resumed. Wierzę, że kiedy próbuję uruchomić własny looper, system wstrzymuje czynność, która ma się rozpocząć? Nie jestem pewien, ale każda pomoc byłaby mile widziana. Dzięki
sttaq
również jeśli usunę startCatcher, tj. uruchomię aplikację bez twojego kodu, wszystko działa dobrze bez żadnych wyjątków.
sttaq
@sttaq Jakiej wersji Androida używasz?
Idolon
Myślę, że próbowałem tego 2.3.x
sttaq
3

Myślę, że aby wyłączyć to w twojej metodzie uncaughtException (), nie wywołuj previousHandler.uncaughtException (), gdzie parametr previousHandler jest ustawiony przez

previousHandler = Thread.getDefaultUncaughtExceptionHandler();
Robby Pond
źródło
2

FWIW Wiem, że to trochę nie na temat, ale z powodzeniem korzystamy z darmowego planu Crittercism . Oferują również pewne funkcje premium, takie jak obsługa wyjątku, aby aplikacja nie uległa awarii.

W wersji bezpłatnej użytkownik nadal widzi awarię, ale przynajmniej otrzymuję wiadomość e-mail i ślad stosu.

Korzystamy również z wersji na iOS (ale słyszałem od kolegów, że nie jest aż tak dobra).


Oto podobne pytania:

Richard Le Mesurier
źródło
1

Nie działa, dopóki nie zadzwonisz

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

na samym końcu Twojego UncaughtExceptionHandler.

Joseph_Marzbani
źródło