Rysunek 1. Okno błędu ANR wyświetlane użytkownikowi.
Z tego dokumentu dowiesz się, jak system Android określa, czy aplikacja nie odpowiada, oraz jak zapewnić jej responsywność.
Nawet jeśli kod jest dobrze napisany, aplikacja może działać powoli, zawieszać się, zamrażać na dłuższy czas lub zbyt długo przetwarzać dane wejściowe. Jeśli aplikacja działa na pierwszym planie i nie odpowiada, użytkownik zobaczy okno „Aplikacja nie odpowiada” (ANR), jak pokazano na rysunku 1. Okno ANR umożliwia użytkownikowi wymuszenie zamknięcia aplikacji. Jeśli aplikacja nie działa na pierwszym planie, zostanie zatrzymana w tle. Aby zminimalizować liczbę okien ANR, musisz zaprojektować aplikację tak, aby była responsywna.
Aktywatory błędów ANR
Zazwyczaj system wyświetla błąd ANR, gdy aplikacja nie może odpowiedzieć na dane wejściowe użytkownika w wątku głównym (zwanym też wątkiem UI), co uniemożliwia systemowi przetwarzanie przychodzących zdarzeń z udziałem użytkownika.
Błąd ANR może wystąpić na przykład wtedy, gdy aplikacja wykonuje blokującą operację wejścia/wyjścia, np. dostęp do sieci, w wątku UI. Innym przykładem jest sytuacja, gdy aplikacja spędza zbyt dużo czasu na tworzeniu złożonej struktury w pamięci lub obliczaniu następnego ruchu w grze w wątku UI.
W Androidzie responsywność aplikacji jest monitorowana przez usługi systemowe ActivityManager i
WindowManager. Android wyświetla okno ANR, gdy wykryje jeden z tych warunków:
- Brak odpowiedzi na zdarzenie wejściowe (np. naciśnięcie klawisza lub dotknięcie ekranu) w ciągu 5 sekund.
- A
BroadcastReceivernie kończy wykonywania w ciągu 10–20 sekund w przypadku intencji działających na pierwszym planie. Więcej informacji znajdziesz w artykule o przekroczeniu limitu czasu odbiornika transmisji.
Unikanie błędów ANR
Oto ogólne wskazówki, jak unikać błędów ANR. Więcej informacji o diagnozowaniu i debugowaniu różnych typów błędów ANR znajdziesz na innych stronach w tej sekcji.
Zadbaj o to, aby wątek główny był zawsze odblokowany, i używaj wątków strategicznie.
Nie wykonuj blokujących ani długotrwałych operacji w wątku głównym aplikacji. Zamiast tego używaj współprogramów Kotlin, aby odciążyć pracę do dyspozytorów w tle (np.
Dispatchers.IOlubDispatchers.Default). Używaj mechanizmów takich jakviewModelScope, aby bezpiecznie uruchamiać te zadania w tle, lubLaunchedEffect, aby wywoływać je w odpowiedzi na zmiany stanu Compose.Postaraj się zminimalizować rywalizację o blokady między wątkiem głównym a innymi wątkami.
Zminimalizuj ilość pracy niezwiązanej z interfejsem wykonywanej w wątku głównym, np. podczas obsługi transmisji lub uruchamiania usług. Każda metoda lub funkcja działająca w wątku interfejsu musi wykonywać jak najmniej pracy. W szczególności aktywności muszą wykonywać jak najmniej pracy podczas konfigurowania w kluczowych metodach cyklu życia, takich jak
onCreateionResume. Nigdy nie wykonuj operacji wejścia/wyjścia ani złożonych, blokujących obliczeń bezpośrednio w funkcji typu „composable”. Blokuje to wątek interfejsu podczas kompozycji i rekompozycji. Więcej informacji o dostępnych rozwiązaniach do planowania pracy w wątku w tle i komunikowania się z interfejsem znajdziesz w artykule Omówienie zadań w tle.Zachowaj ostrożność podczas udostępniania pul wątków między komponentami. Nie używaj tych samych wątków do potencjalnie długotrwałych operacji blokujących i zadań wrażliwych na czas, takich jak odbieranie transmisji.
Zadbaj o szybkie uruchamianie aplikacji. Zminimalizuj powolne lub blokujące operacje w kodzie uruchamiania aplikacji, takie jak metody uruchamiane podczas konfigurowania wstrzykiwania zależności (np. za pomocą Hilt) lub komponenty inicjowane za pomocą biblioteki Jetpack App Startup. Możesz dodatkowo zoptymalizować uruchamianie aplikacji za pomocą profili podstawowych, profili uruchamiania i R8.
Jeśli używasz
BroadcastReceiver, rozważ uruchamianie odbiorników transmisji w wątku innym niż główny za pomocąContext.registerReceiver. Więcej informacji znajdziesz w artykule Błędy ANR w BroadcastReceiver.- Jeśli używasz
goAsync, upewnij się, żePendingResult.finishjest wywoływana szybko przed przekroczeniem limitu czasu ANR.
- Jeśli używasz
Błędy ANR w BroadcastReceiver
Czas wykonywania BroadcastReceiver jest ograniczony, ponieważ odbiorniki transmisji
mają wykonywać niewielkie, dyskretne ilości pracy w tle, np.
zapisywać ustawienie lub rejestrować Notification. Podobnie jak w przypadku innych metod wywoływanych w wątku UI, aplikacje muszą unikać potencjalnie długotrwałych operacji lub obliczeń w odbiorniku transmisji. Zamiast wykonywać długotrwałe zadania w wątku UI, wykonuj je w tle, aby można je było uruchomić później. Więcej informacji o
możliwych rozwiązaniach znajdziesz w artykule Omówienie zadań w tle.
Inny częsty problem z obiektami BroadcastReceiver występuje, gdy są one wykonywane zbyt często. Częste wykonywanie w tle może zmniejszyć ilość pamięci dostępnej dla innych aplikacji. Więcej informacji o tym, jak skutecznie włączać i wyłączać
BroadcastReceiver obiekty, znajdziesz w artykule Omówienie transmisji.
Zwiększanie responsywności
Zazwyczaj próg, powyżej którego użytkownicy postrzegają aplikację jako powolną, wynosi 100–200 ms. Oto dodatkowe wskazówki, jak sprawić, aby aplikacja wydawała się responsywna:
Jeśli aplikacja wykonuje pracę w tle w odpowiedzi na dane wejściowe użytkownika, pokaż że postęp jest realizowany, np. za pomocą
CircularProgressIndicatorlubLinearProgressIndicatorw interfejsie.W przypadku gier obliczenia ruchów wykonuj w korutynie w tle lub w wątku roboczym.
Jeśli aplikacja ma czasochłonny etap konfiguracji początkowej, rozważ wyświetlenie ekranu powitalnego lub jak najszybsze renderowanie kompozycji początkowej. Wskaż, że trwa wczytywanie, i asynchronicznie wypełnij stan interfejsu. W obu przypadkach zalecamy, aby w jakiś sposób wskazać, że postęp jest realizowany, aby użytkownik nie miał wrażenia, że aplikacja się zawiesiła.
Użyj narzędzi do analizy wydajności, takich jak Perfetto i CPU Profiler, aby określić wąskie gardła w responsywności aplikacji.