Zadbaj o elastyczność aplikacji

Rysunek 1. Użytkownik zobaczył okno z komunikatem o błędzie ANR.

W tym dokumencie opisujemy, jak system Android określa, czy aplikacja nie odpowiada, oraz jak zadbać o responsywność aplikacji.

Niezależnie od tego, jak dobrze napisany jest Twój kod, aplikacja może się zawieszać, zawieszać się, zawieszać się przez 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 ANR (Aplikacja nie odpowiada), jak widać na ilustracji 1. Okno ANR pozwala użytkownikowi wymusić zamknięcie aplikacji. Jeśli aplikacji nie ma na pierwszym planie, zostanie ona zatrzymana dyskretnie. Bardzo ważne jest zaprojektowanie reagowania aplikacji w celu zminimalizowania liczby okien błędów ANR.

Wyzwalacze błędów ANR

System wyświetla błąd ANR, jeśli aplikacja nie może odpowiedzieć na działania użytkownika w wątku głównym – czyli w wątku interfejsu – co uniemożliwia systemowi przetwarzanie przychodzących zdarzeń wejściowych użytkownika.

Na przykład błąd ANR może wystąpić, gdy aplikacja wykonuje operację blokowania wejścia-wyjścia (np. dostęp do sieci) w wątku interfejsu użytkownika. Innym przykładem jest sytuacja, w której aplikacja poświęca zbyt dużo czasu na tworzenie rozbudowanej struktury w pamięci lub obliczanie następnego ruchu w grze w wątku interfejsu.

Na Androidzie czas reagowania aplikacji jest monitorowany przez usługi systemowe ActivityManager i WindowManager. Android wyświetla okno ANR aplikacji, gdy wykryje jeden z tych warunków:

  • Brak reakcji na zdarzenie wejściowe – takie jak naciśnięty klawisz lub zdarzenie kliknięcia ekranu – w ciągu 5 sekund.
  • W przypadku intencji na pierwszym planie BroadcastReceiver nie kończy wykonywania w ciągu 10–20 sekund. Więcej informacji znajdziesz w artykule Limit czasu odbiornika transmisji.

Unikanie błędów ANR

Oto ogólne wskazówki dotyczące unikania 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.

  • Wątek główny powinien być zawsze odblokowany, a wątki należy traktować strategicznie.

    • Nie wykonuj blokowania ani długo trwających operacji w wątku głównym aplikacji. Zamiast tego utwórz wątek instancji roboczej i wykonaj w nim większość pracy.

    • Postaraj się zminimalizować rywalizację o blokadę między wątkiem głównym a innymi wątkami.

    • Zminimalizuj wszelkie czynności niezwiązane z interfejsem w wątku głównym, na przykład przy obsłudze transmisji lub uruchamiania usług. Każda metoda stosowana w wątku UI musi wykonywać w nim jak najmniejszą pracę. W szczególności działania muszą wykonywać tylko tyle, ile to możliwe, aby można było skonfigurować je w kluczowych metodach cyklu życia, takich jak onCreate() i onResume(). W artykule Omówienie zadań w tle znajdziesz więcej informacji o dostępnych rozwiązaniach do planowania pracy w tle i komunikowania się z interfejsem użytkownika.

    • 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 i zadań wymagających szczególnego czasu, takich jak odbieranie transmisji.

  • Zadbaj o szybkie uruchamianie aplikacji Zminimalizuj powolne lub blokujące działanie w kodzie startowym aplikacji, np. metody uruchamiane podczas inicjowania sztyletu.

  • Jeśli używasz BroadcastReceiver, rozważ uruchomienie odbiorników w wątku innym niż główny za pomocą funkcji Context.registerReceiver. Więcej informacji znajdziesz w artykule o błędach ANR w Broadcastpickupr.

    • Jeśli używasz funkcji goAsync(), zadbaj o to, aby funkcja PendingResult.finish była wywoływana szybko przed upływem limitu czasu błędu ANR.

Błędy ANR w BroadcastReceivedr

Czas wykonywania BroadcastReceiver jest ograniczony, ponieważ odbiorniki umożliwiają wykonywanie niewielkich, dyskretnych zadań w tle, takich jak zapisywanie ustawień lub rejestrowanie Notification. Podobnie jak w przypadku innych metod wywoływanych w wątku interfejsu użytkownika, aplikacje muszą unikać potencjalnie długotrwałych operacji lub obliczeń w odbiorniku. Zamiast wykonywać długotrwałe zadania w wątku interfejsu użytkownika, możesz je wykonywać w tle, aby móc je później wykonać. Więcej informacji o możliwych rozwiązaniach znajdziesz w artykule Omówienie prac w tle.

Kolejny typowy problem z obiektami BroadcastReceiver występuje, gdy są one wykonywane zbyt często. Częste wykonywanie kodu w tle może zmniejszyć ilość pamięci dostępnej dla innych aplikacji. Więcej informacji o efektywnym włączaniu i wyłączaniu obiektów BroadcastReceiver znajdziesz w artykule Omówienie komunikatów.

Zwiększ czas reagowania

Zazwyczaj czas od 100 do 200 ms to próg, po przekroczeniu którego użytkownicy postrzegają powolne działanie aplikacji. Oto dodatkowe wskazówki, dzięki którym aplikacja będzie sprawiała wrażenie responsywnej:

  • Jeśli Twoja aplikacja działa w tle w odpowiedzi na działania użytkownika, informuj o postępach, np. za pomocą ikony ProgressBar w interfejsie.

  • W przypadku gier – obliczenia dotyczące ruchów w wątku instancji roboczych.

  • Jeśli etap wstępnej konfiguracji Twojej aplikacji jest czasochłonny, możesz wyświetlić ekran powitalny lub jak najszybciej wyrenderować widok główny. Wskaż, że trwa ładowanie, i podaj informacje asynchronicznie. W obu przypadkach zalecamy poinformowanie użytkownika, w jaki sposób aplikacja się zatrzymuje.

  • Użyj narzędzi zwiększających wydajność, takich jak Perfetto i CPU Profiler, aby określić wąskie gardła reakcji aplikacji.