Procesy i cykl życia aplikacji

W większości przypadków każda aplikacja na Androida działa w ramach własnego procesu Linuksa. Ten proces jest tworzony dla aplikacji, gdy część jej kodu musi działać, i pozostawiany jest w stanie uruchomionym do momentu, gdy system potrzebuje jego pamięci do użycia przez inne aplikacje i nie jest już potrzebny.

Niezwykłą i podstawową cechą Androida jest to, że czas trwania procesu aplikacji nie jest kontrolowany bezpośrednio przez samą aplikację. Zamiast tego jest ona określana przez system na podstawie kombinacji części aplikacji, które system wie, że są uruchomione, ich znaczenia dla użytkownika oraz ogólnej ilości pamięci dostępnej w systemie.

Ważne jest, aby deweloperzy aplikacji rozumieli, jak różne komponenty aplikacji (szczególnie Activity, ServiceBroadcastReceiver) wpływają na czas trwania procesu aplikacji. Nieprawidłowe korzystanie z tych komponentów może spowodować, że system zakończy proces aplikacji w trakcie wykonywania ważnej pracy.

Typowym przykładem błędu cyklu życia procesu jest BroadcastReceiver, który uruchamia wątek, gdy w metodzie BroadcastReceiver.onReceive() otrzyma parametr Intent, a potem zwraca z tej funkcji. Gdy BroadcastReceiver wróci, system uzna, że nie jest już aktywny, a proces hostowania nie jest już potrzebny, chyba że inne komponenty aplikacji są w nim aktywne.

System może w dowolnym momencie zakończyć proces, aby odzyskać pamięć. W ten sposób kończy również nić potomna uruchomioną w ramach procesu. Rozwiązaniem tego problemu jest zazwyczaj zaplanowanie JobService z BroadcastReceiver, aby system wiedział, że w ramach tego procesu są wykonywane aktywne działania.

Aby określić, które procesy należy zakończyć, gdy brakuje pamięci, Android umieszcza każdy proces w hierarchii ważności na podstawie komponentów, które są w nich uruchamiane, oraz stanu tych komponentów. W kolejności od najważniejszych:

  1. Proces w tle to proces wymagany do tego, co użytkownik obecnie robi. Różne komponenty aplikacji mogą powodować, że proces, który je zawiera, jest uważany za proces czołowy na różne sposoby. Proces jest uznawany za działający na pierwszym planie, jeśli spełnia co najmniej jeden z tych warunków:
  2. W systemie jest tylko kilka takich procesów, które są zabijane tylko w ostatniej chwili, gdy ilość pamięci jest tak mała, że nawet te procesy nie mogą działać. Zwykle, gdy tak się dzieje, urządzenie osiąga stan strony pamięci, więc to działanie jest wymagane, aby zachować responsywność interfejsu.

  3. Proces widoczny wykonuje zadanie, o którym użytkownik jest obecnie informowany, więc jego przerwanie ma zauważalny negatywny wpływ na wrażenia użytkownika. Proces jest uważany za widoczny w tych warunkach:
    • Użytkownik widzi na ekranie widoczną aplikację Activity, ale nie jest ona na pierwszym planie (została wywołana metoda onPause()). Może się to zdarzyć, jeśli Activity na pierwszym planie jest wyświetlane jako okno dialogowe, które pozwala zobaczyć poprzednie Activity.
    • Aplikacja ma usługę Service, która działa jako usługa na pierwszym planie, za pomocą Service.startForeground() (która prosi system o traktowanie usługi jako czegoś, o czym użytkownik wie, lub tak, jakby była widoczna).
    • Usługa hostowana przez system do obsługi określonej funkcji, o której użytkownik jest świadomy, takiej jak tapeta na żywo lub usługa metody wprowadzania.

    Liczba tych procesów działających w systemie jest mniej ograniczona niż w przypadku procesów na pierwszym planie, ale nadal jest stosunkowo kontrolowana. Te procesy są uważane za bardzo ważne i nie są zabijane, chyba że jest to konieczne do utrzymania wszystkich procesów na pierwszym planie.

  4. Proces usługi to proces zawierający Service, który został uruchomiony za pomocą metody startService(). Chociaż te procesy nie są bezpośrednio widoczne dla użytkownika, zwykle wykonują czynności, które są dla niego ważne (np. przesyłają lub pobierają dane sieci w tle), więc system zawsze je utrzymuje, chyba że nie ma wystarczającej ilości pamięci na wszystkie procesy na pierwszym i drugim planie.

    Usługi, które działają od dłuższego czasu (np. od 30 minut lub dłużej), mogą zostać zdegradowane, aby ich procesy zostały przeniesione na listę z pamięci podręcznej.

    Za pomocą setForeground możesz tworzyć procesy, które muszą być wykonywane przez dłuższy czas. Jeśli jest to proces okresowy, który wymaga ścisłego określenia czasu wykonania, można go zaplanować za pomocą AlarmManager. Więcej informacji znajdziesz w artykule Wsparcie dla długotrwałych procesów. Pomaga to uniknąć sytuacji, w której długotrwałe usługi, które wykorzystują nadmierne zasoby (np. przez wyciek pamięci), uniemożliwiają systemowi zapewnienie dobrych wrażeń użytkownikom.

  5. Przechowywany w pamięci podręcznej proces to proces, który nie jest obecnie potrzebny, więc system może go w razie potrzeby zakończyć, gdy zasoby, takie jak pamięć, są potrzebne gdzie indziej. W systemie działającym prawidłowo są to jedyne procesy związane z zarządzaniem zasobami.

    Dobrze działający system ma wiele procesów z pamięci podręcznej, które są zawsze dostępne, aby umożliwić sprawne przełączanie się między aplikacjami. Regularnie zabija też aplikacje z pamięci podręcznej w miarę potrzeby. Tylko w bardzo krytycznych sytuacjach system dochodzi do punktu, w którym wszystkie procesy buforowane są zabijane i musi zacząć zabijać procesy usług.

    Procesy w pamięci podręcznej mogą zostać w dowolnym momencie przerwane przez system, dlatego aplikacje powinny przestać działać, gdy są w stanie pamięci podręcznej. Jeśli aplikacja musi wykonać zadanie krytyczne dla użytkownika, powinna użyć jednego z wymienionych wyżej interfejsów API, aby wykonać zadanie w stanie aktywnego procesu.

    Procesy w pamięci podręcznej często zawierają co najmniej 1 instancję Activity, która nie jest obecnie widoczna dla użytkownika (metoda onStop() została wywołana i zwróciła wartość). Jeśli system zakończy działanie takich procesów, a aplikacja Activity zostanie prawidłowo zaimplementowana, nie wpłynie to na komfort użytkownika po powrocie do aplikacji. Aplikacja może przywrócić wcześniej zapisany stan, gdy powiązana aktywność zostanie ponownie utworzona w nowym procesie. Pamiętaj, że wywołanie funkcji onDestroy()nie jest gwarantowane w przypadku, gdy proces zostanie przerwany przez system. Więcej informacji znajdziesz w artykule Activity.

    Począwszy od Androida 13 proces aplikacji może mieć ograniczony czas wykonywania lub nie mieć go wcale, dopóki nie wejdzie w jeden z wymienionych powyżej stanów aktywnego cyklu życia.

    Procesy z pamięci podręcznej są przechowywane na liście. Dokładna zasada określania kolejności na tej liście jest szczegółem implementacji platformy. Zazwyczaj stara się zachować przydatniejsze procesy, takie jak te, które obsługują aplikację domową użytkownika lub ostatnią aktywność użytkownika, przed procesami innych typów. Możesz też zastosować inne zasady dotyczące zabijania procesów, np. ustawić twarde limity liczby dozwolonych procesów lub ograniczyć czas, przez jaki proces może pozostawać w pamięci podręcznej.

Przy podejmowaniu decyzji o sklasyfikowaniu procesu system kieruje się najważniejszym poziomem spośród wszystkich komponentów aktywnych w danym procesie. Więcej informacji o tym, jak poszczególne komponenty wpływają na cały cykl życia procesu i aplikacji, znajdziesz w dokumentacji Activity, ServiceBroadcastReceiver.

Priorytet procesu może też wzrosnąć na podstawie innych zależności, które ten proces ma. Jeśli na przykład proces A jest powiązany z elementem Service z flagą Context.BIND_AUTO_CREATE lub używa elementu ContentProvider w procesie B, klasyfikacja procesu B jest zawsze co najmniej tak ważna jak klasyfikacja procesu A.