Android - jak zbadać błąd ANR?

153

Czy istnieje sposób na sprawdzenie, gdzie moja aplikacja zgłosiła błąd ANR (aplikacja nie odpowiada)? Rzuciłem okiem na plik traces.txt w / data i widzę ślad dla mojej aplikacji. Oto, co widzę w śladzie.

DALVIK THREADS:
"main" prio=5 tid=3 TIMED_WAIT
  | group="main" sCount=1 dsCount=0 s=0 obj=0x400143a8
  | sysTid=691 nice=0 sched=0/0 handle=-1091117924
  at java.lang.Object.wait(Native Method)
  - waiting on <0x1cd570> (a android.os.MessageQueue)
  at java.lang.Object.wait(Object.java:195)
  at android.os.MessageQueue.next(MessageQueue.java:144)
  at android.os.Looper.loop(Looper.java:110)
  at android.app.ActivityThread.main(ActivityThread.java:3742)
  at java.lang.reflect.Method.invokeNative(Native Method)
  at java.lang.reflect.Method.invoke(Method.java:515)
  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:739)
  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:497)
  at dalvik.system.NativeStart.main(Native Method)

"Binder Thread #3" prio=5 tid=15 NATIVE
  | group="main" sCount=1 dsCount=0 s=0 obj=0x434e7758
  | sysTid=734 nice=0 sched=0/0 handle=1733632
  at dalvik.system.NativeStart.run(Native Method)

"Binder Thread #2" prio=5 tid=13 NATIVE
  | group="main" sCount=1 dsCount=0 s=0 obj=0x433af808
  | sysTid=696 nice=0 sched=0/0 handle=1369840
  at dalvik.system.NativeStart.run(Native Method)

"Binder Thread #1" prio=5 tid=11 NATIVE
  | group="main" sCount=1 dsCount=0 s=0 obj=0x433aca10
  | sysTid=695 nice=0 sched=0/0 handle=1367448
  at dalvik.system.NativeStart.run(Native Method)

"JDWP" daemon prio=5 tid=9 VMWAIT
  | group="system" sCount=1 dsCount=0 s=0 obj=0x433ac2a0
  | sysTid=694 nice=0 sched=0/0 handle=1367136
  at dalvik.system.NativeStart.run(Native Method)

"Signal Catcher" daemon prio=5 tid=7 RUNNABLE
  | group="system" sCount=0 dsCount=0 s=0 obj=0x433ac1e8
  | sysTid=693 nice=0 sched=0/0 handle=1366712
  at dalvik.system.NativeStart.run(Native Method)

"HeapWorker" daemon prio=5 tid=5 VMWAIT
  | group="system" sCount=1 dsCount=0 s=0 obj=0x4253ef88
  | sysTid=692 nice=0 sched=0/0 handle=1366472
  at dalvik.system.NativeStart.run(Native Method)

----- end 691 -----

Jak mogę się dowiedzieć, gdzie jest problem? Wszystkie metody śledzenia są metodami zestawu SDK.

Dzięki.

lostInTransit
źródło
2
Mam jeden taki raport, również o android.os.MessageQueue.nativePollOnce(Native Method). Czy mogę bezpiecznie to zignorować?
rds

Odpowiedzi:

124

Błąd ANR występuje, gdy w głównym wątku ma miejsce długa operacja. To jest wątek pętli zdarzeń, a jeśli jest zajęty, system Android nie może przetwarzać żadnych dalszych zdarzeń GUI w aplikacji i dlatego wyświetla okno dialogowe ANR.

Teraz, w opublikowanym śladzie, główny wątek wydaje się działać dobrze, nie ma problemu. Jest na biegu jałowym w MessageQueue, czekając na nadejście kolejnej wiadomości. W Twoim przypadku błąd ANR był prawdopodobnie dłuższą operacją, a nie czymś, co trwale zablokowało wątek, więc wątek zdarzenia został odtworzony po zakończeniu operacji, a ślad przeszedł po ANR.

Wykrywanie, gdzie występują błędy ANR, jest łatwe, jeśli jest to trwała blokada (na przykład zakleszczenie w celu uzyskania niektórych blokad), ale trudniejsze, jeśli jest to tylko tymczasowe opóźnienie. Najpierw przejrzyj swój kod i poszukaj miejsc podlegających vunerowi i długotrwałych operacji. Przykłady mogą obejmować używanie gniazd, blokad, uśpień wątków i innych operacji blokujących z poziomu wątku zdarzeń. Powinieneś upewnić się, że wszystko to dzieje się w osobnych wątkach. Jeśli nic nie wydaje się problemem, użyj DDMS i włącz widok wątku. To pokazuje wszystkie wątki w twojej aplikacji podobne do śladu, który masz. Odtwórz błąd ANR i odśwież główny wątek w tym samym czasie. To powinno dokładnie pokazać, co się dzieje w czasie błędu ANR

wkrótce
źródło
6
jedyny problem to „odtworzyć ANR” :-). czy mógłbyś proszę wyjaśnić, w jaki sposób główny wątek programu śledzenia stosu jest „bezczynny”, byłoby świetnie.
Blundell,
20
Ślad stosu pokazuje, że główny wątek znajduje się w pętli Looper (implementacja pętli komunikatów) i wykonuje czasowe oczekiwanie przez Object.wait. Oznacza to, że pętle komunikatów nie mają obecnie żadnych komunikatów do wysłania i czekają na nadejście nowych komunikatów. Błąd ANR występuje, gdy system zda sobie sprawę, że pętla komunikatów spędza zbyt dużo czasu na przetwarzaniu komunikatu i nie przetwarzaniu innych komunikatów w kolejka. Jeśli pętle czekają na wiadomości, oczywiście tak się nie dzieje.
wkrótceiln
3
@Soonil Cześć, czy wiesz, co oznaczają pozostałe sekcje, takie jak wątek Binder 3, wątek Binder 2 JDWP demon prio 5. co to jest sCount, dsCount, obj, sysTid, nice schedule oznacza. ma również informacje takie jak VMWAIT, RUNNABLE, NATIVE
minhaz,
1
Moja aplikacja jest oparta na NDK. Widzę ten sam błąd ANR. Również główny wątek jest w porządku. Wypróbowałem DDMS i odświeżyłem wątek roboczy, gdy się zawiesił. Niestety wszystko co otrzymuję to pojedyncza linia NativeStart :: run. Czy widok wątków DDMS może nawet sprawdzać natywne wątki NDK? Ponadto: StrictMode nic nie znalazł.
Bram
6
Zobacz elliotth.blogspot.com/2012/08/…, aby uzyskać dobre wyjaśnienie wyniku.
niedługo
96

Możesz włączyć StrictMode na poziomie API 9 i wyższym.

StrictMode jest najczęściej używany do przechwytywania przypadkowego dostępu do dysku lub sieci w głównym wątku aplikacji, w którym są odbierane operacje interfejsu użytkownika i odbywają się animacje. Utrzymując responsywność głównego wątku aplikacji, zapobiegasz także wyświetlaniu okien dialogowych ANR użytkownikom.

public void onCreate() {
    StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
                           .detectAll()
                           .penaltyLog()
                           .penaltyDeath()
                           .build());
    super.onCreate();
}

Używając penaltyLog()możesz obserwować dane wyjściowe adb logcat podczas korzystania z aplikacji, aby zobaczyć występujące naruszenia.

Dheeraj Vepakomma
źródło
StrictMode nie można przekształcić w typ. Czy jest coś, co muszę najpierw zaimportować? Naciśnięcie CTRL + SHIFT + O nie pomaga.
kuchi
23
mała wskazówka - użyj if (BuildConfig.DEBUG) ..., aby zapobiec włączeniu do produkcji
Amir Uval
@uval, co rozumiesz przez „zapobieganie włączeniu do produkcji”? !!
Muhammed Refaat
2
@MuhammedRefaat to nie zapobiega żadnemu ANR. Spowoduje to awarię aplikacji natychmiast zamiast po 5 sekundach. Na przykład, jeśli uzyskasz dostęp do bazy danych w głównym wątku i zajmie to 2 sekundy, nie otrzymasz ANR, ale StrictMode spowoduje awarię aplikacji. StrictMode jest przeznaczony wyłącznie do fazy debugowania, a nie do produkcji.
Amir Uval
1
@MuhammedRefaat dodał moją odpowiedź na twoje pytanie.
Amir Uval
80

Zastanawiasz się, które zadanie zawiera wątek interfejsu użytkownika. Plik śledzenia daje wskazówkę, jak znaleźć zadanie. musisz zbadać stan każdego wątku

Stan wątku

  • bieganie - wykonywanie kodu aplikacji
  • spanie - nazywane Thread.sleep ()
  • monitor - oczekiwanie na uzyskanie blokady monitora
  • wait - w Object.wait ()
  • native - wykonywanie kodu natywnego
  • vmwait - oczekiwanie na zasób maszyny wirtualnej
  • zombie - nić umiera
  • init - inicjalizacja wątku (nie powinieneś tego widzieć)
  • start - wątek zaraz się rozpocznie (nie powinieneś tego widzieć)

Skoncentruj się na stanie ZAWIESZONY, MONITOROWY. Stan monitora wskazuje, który wątek jest badany, a stan ZAWIESZENIA wątku jest prawdopodobnie główną przyczyną zakleszczenia.

Podstawowe kroki badawcze

  1. Znajdź „czekam na zamknięcie”
    • możesz znaleźć stan monitora „Binder Thread # 15” prio = 5 tid = 75 MONITOR
    • masz szczęście, jeśli znajdziesz „czekam na zamknięcie”
    • przykład: oczekiwanie na zablokowanie <0xblahblah> (com.foo.A) utrzymywane przez threadid = 74
  2. Możesz zauważyć, że „tid = 74” wstrzymuje teraz zadanie. Więc idź do tid = 74
  3. tid = 74 może stan ZAWIESZONY! znajdź główny powód!

ślad nie zawsze zawiera „oczekiwanie na zablokowanie”. w tym przypadku trudno znaleźć główny powód.

Horyun Lee
źródło
1
Niezłe wyjaśnienie. Teraz łatwiej mi zrozumieć dzienniki ANR. Ale nadal mam problem ze zrozumieniem przyczyny, ponieważ w kroku 1 jestem w stanie łatwo znaleźć identyfikator wątku, ale kiedy w kroku 2 próbuję iść tam, gdzie jest, aby sprawdzić stan, nie mogę go znaleźć . Masz jakiś pomysł, jak to zrobić?
THZ
1
Mam w - waiting to lock an unknown objectśrodku "HeapTaskDaemon" daemon prio=5 tid=8 Blocked . Co to znaczy, czy ktoś może pomóc?
Hilal
13

Uczyłem się Androida przez ostatnie kilka miesięcy, więc daleko mi do eksperta, ale byłem bardzo rozczarowany dokumentacją dotyczącą błędów ANR.

Większość porad wydaje się być ukierunkowana na unikanie ich lub naprawianie przez ślepe przeglądanie kodu, co jest świetne, ale nie mogłem znaleźć nic na temat analizy śladu.

Są trzy rzeczy, których naprawdę musisz szukać w dziennikach ANR.

1) Zakleszczenia: Gdy wątek jest w stanie OCZEKIWANIA, możesz przejrzeć szczegóły, aby znaleźć osobę, która jest „holdby =”. Przez większość czasu będzie trzymany samodzielnie, ale jeśli jest trzymany przez inną nitkę, prawdopodobnie będzie to znak niebezpieczeństwa. Idź, spójrz na tę nić i zobacz, na czym ona polega. Możesz znaleźć pętlę, która jest wyraźnym znakiem, że coś poszło nie tak. Jest to dość rzadkie, ale to pierwsza kwestia, ponieważ kiedy to się dzieje, jest to koszmar

2) Główny wątek Oczekiwanie: Jeśli twój główny wątek jest w stanie WAIT, sprawdź, czy nie jest wstrzymany przez inny wątek. To nie powinno się zdarzyć, ponieważ Twój wątek interfejsu użytkownika nie powinien być utrzymywany przez wątek w tle.

Oba te scenariusze oznaczają, że musisz znacznie przerobić kod.

3) Ciężkie operacje w głównym wątku: jest to najczęstsza przyczyna błędów ANR, ale czasami jedna z trudniejszych do znalezienia i naprawienia. Spójrz na główne szczegóły wątku. Przewiń w dół ślad stosu, aż zobaczysz klasy, które rozpoznajesz (z Twojej aplikacji). Przyjrzyj się metodom śledzenia i dowiedz się, czy w tych miejscach wykonujesz połączenia sieciowe, wywołania bazy danych itp.

Na koniec przepraszam za bezwstydne podłączenie mojego własnego kodu, możesz skorzystać z analizatora dzienników Pythona, który napisałem na https://github.com/HarshEvilGeek/Android-Log-Analyzer To przejdzie przez twoje pliki dziennika, otwórz pliki ANR, znajdź zakleszczenia, znajdowanie oczekujących wątków głównych, znajdowanie nieprzechwyconych wyjątków w dziennikach agentów i drukowanie wszystkiego na ekranie w stosunkowo łatwy do odczytania sposób. Przeczytaj plik ReadMe (który za chwilę dodam), aby dowiedzieć się, jak go używać. Pomogło mi to w ostatnim tygodniu!

Akhil Cherian Verghese
źródło
4

Ilekroć analizujesz problemy z synchronizacją, debugowanie często nie pomaga, ponieważ zawieszenie aplikacji w punkcie przerwania sprawi, że problem zniknie.

Najlepszym rozwiązaniem jest wstawienie wielu wywołań rejestrowania (Log.XXX ()) do różnych wątków i wywołań zwrotnych aplikacji i sprawdzenie, gdzie jest opóźnienie. Jeśli potrzebujesz śledzenia stosu, utwórz nowy wyjątek (po prostu utwórz jeden) i zarejestruj go.

Ulrich
źródło
2
Dziękuję za radę dotyczącą tworzenia nowego wyjątku, jeśli potrzebujesz śledzenia stosu. To jest bardzo pomocne przy debugowaniu :)
kuchi
3

Co wyzwala ANR?

Zazwyczaj system wyświetla błąd ANR, jeśli aplikacja nie może odpowiedzieć na dane wejściowe użytkownika.

W każdej sytuacji, w której aplikacja wykonuje potencjalnie długotrwałą operację, nie należy wykonywać pracy w wątku interfejsu użytkownika, ale zamiast tego utworzyć wątek roboczy i wykonać tam większość pracy. Dzięki temu wątek interfejsu użytkownika (który steruje pętlą zdarzeń interfejsu użytkownika) działa i zapobiega stwierdzeniu przez system, że kod został zamrożony.

Jak uniknąć błędów ANR

Aplikacje dla systemu Android zwykle działają całkowicie w jednym wątku, domyślnie „wątek interfejsu użytkownika” lub „wątek główny”). Oznacza to, że wszystko, co aplikacja robi w wątku interfejsu użytkownika, a wykonanie tego zabiera dużo czasu, może wywołać okno dialogowe ANR, ponieważ aplikacja nie daje sobie szansy na obsługę zdarzenia wejściowego lub rozgłaszania zamiaru.

W związku z tym każda metoda, która działa w wątku interfejsu użytkownika, powinna wykonywać jak najmniej pracy w tym wątku. W szczególności działania powinny w jak najmniejszym stopniu konfigurować się w kluczowych metodach cyklu życia, takich jak onCreate () i onResume (). Potencjalnie długotrwałe operacje, takie jak operacje sieciowe lub bazodanowe, lub obliczenia kosztowne obliczeniowo, takie jak zmiana rozmiaru map bitowych, powinny być wykonywane w wątku roboczym (lub w przypadku operacji na bazach danych za pośrednictwem żądania asynchronicznego).

Kod: wątek roboczy z klasą AsyncTask

private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
    // Do the long-running work in here
    protected Long doInBackground(URL... urls) {
        int count = urls.length;
        long totalSize = 0;
        for (int i = 0; i < count; i++) {
            totalSize += Downloader.downloadFile(urls[i]);
            publishProgress((int) ((i / (float) count) * 100));
            // Escape early if cancel() is called
            if (isCancelled()) break;
        }
        return totalSize;
    }

    // This is called each time you call publishProgress()
    protected void onProgressUpdate(Integer... progress) {
        setProgressPercent(progress[0]);
    }

    // This is called when doInBackground() is finished
    protected void onPostExecute(Long result) {
        showNotification("Downloaded " + result + " bytes");
    }
}

Kod: wykonanie wątku roboczego

Aby wykonać ten wątek roboczy, po prostu utwórz instancję i wywołaj funkcję execute ():

new DownloadFilesTask().execute(url1, url2, url3);

Źródło

http://developer.android.com/training/articles/perf-anr.html

Jacek
źródło
1

mój problem z ANR, po wielu pracach dowiedziałem się, że wątek wywołuje zasób, którego nie ma w układzie, zamiast zwrócić wyjątek, otrzymałem ANR ...

yaniv
źródło
to jest niezwykle dziwne
Nilabja
0

Na podstawie odpowiedzi @Horyun Lee napisałem mały skrypt w języku Python, aby pomóc zbadać ANR ztraces.txt .

Błędy ANR będą wyświetlane jako grafika, graphvizjeśli zainstalowałeś grapvhvizw systemie.

$ ./anr.py --format png ./traces.txt

Plik png zostanie wyświetlony jak poniżej, jeśli w pliku wykryto błędy ANR traces.txt. Jest bardziej intuicyjny.

wprowadź opis obrazu tutaj

Przykładowy traces.txtplik użyty powyżej został pobrany stąd .

alijandro
źródło
0

Rozważ użycie biblioteki ANR-Watchdog, aby dokładnie śledzić i przechwytywać ślady stosu ANR z dużą szczegółowością. Następnie możesz wysłać je do swojej biblioteki zgłaszania awarii. Polecam używaniesetReportMainThreadOnly() w tym scenariuszu. Możesz sprawić, by aplikacja zgłosiła niekrytyczny wyjątek punktu zamrożenia lub wymusiła zamknięcie aplikacji, gdy wystąpi błąd ANR.

Zwróć uwagę, że standardowe raporty ANR wysyłane do konsoli programisty Google Play często nie są wystarczająco dokładne, aby dokładnie określić problem. Dlatego potrzebna jest biblioteka innej firmy.

Mr-IDE
źródło