Cykl życia aktywności

Gdy użytkownik porusza się po aplikacji, opuszcza ją i do niej wraca, instancje Activity w aplikacji przechodzą przez różne stany w swoim cyklu życia. Klasa Activity udostępnia szereg wywołań zwrotnych, które informują aktywność o zmianie stanu lub o tym, że system tworzy, zatrzymuje lub wznawia aktywność albo niszczy proces, w którym się ona znajduje.

W metodach wywołania zwrotnego cyklu życia możesz zadeklarować, jak aktywność ma się zachowywać, gdy użytkownik ją opuści i ponownie do niej wejdzie. Jeśli na przykład tworzysz odtwarzacz strumieniowego przesyłania filmów, możesz wstrzymać odtwarzanie i zakończyć połączenie sieciowe, gdy użytkownik przełączy się na inną aplikację. Gdy użytkownik wróci, możesz ponownie połączyć się z siecią i umożliwić mu wznowienie odtwarzania filmu od tego samego miejsca.

Każde wywołanie zwrotne umożliwia wykonanie określonej pracy, która jest odpowiednia dla danej zmiany stanu. Wykonywanie odpowiednich zadań we właściwym czasie i prawidłowe obsługiwanie przejść sprawia, że aplikacja jest bardziej niezawodna i wydajna. Na przykład prawidłowa implementacja wywołań zwrotnych cyklu życia może pomóc aplikacji uniknąć:

  • awaria, gdy użytkownik odbiera połączenie telefoniczne lub przełącza się na inną aplikację podczas korzystania z Twojej aplikacji;
  • zużywają cenne zasoby systemowe, gdy użytkownik nie korzysta z nich aktywnie;
  • utratę postępów użytkownika, jeśli opuści on aplikację i wróci do niej później;
  • awaria lub utrata postępów użytkownika, gdy ekran obraca się między orientacją poziomą i pionową;

Ten dokument szczegółowo opisuje cykl życia aktywności. Dokument zaczyna się od opisu paradygmatu cyklu życia. Następnie wyjaśnia poszczególne wywołania zwrotne: co dzieje się wewnętrznie podczas ich wykonywania i co należy zaimplementować podczas ich wykonywania.

Następnie krótko przedstawia związek między stanem aktywności a podatnością procesu na zakończenie przez system. Na koniec omawia kilka tematów związanych z przejściami między stanami aktywności.

Informacje o obsłudze cykli życia, w tym wskazówki dotyczące sprawdzonych metod, znajdziesz w artykułach Cykl życia w Jetpack Compose i Zapisywanie stanów interfejsu. Aby dowiedzieć się, jak zaprojektować solidną aplikację o jakości produkcyjnej, używając aktywności w połączeniu z komponentami architektury, zapoznaj się z Przewodnikiem po architekturze aplikacji.

Pojęcia związane z cyklem życia aktywności

Aby ułatwić przechodzenie między etapami cyklu życia działania, klasa Activity udostępnia podstawowy zestaw 6 wywołań zwrotnych: onCreate, onStart, onResume, onPause, onStop i onDestroy. System wywołuje każde z tych wywołań zwrotnych, gdy aktywność przechodzi w nowy stan.

Rysunek 1 przedstawia wizualną reprezentację tego paradygmatu.

Rysunek 1. Uproszczona ilustracja cyklu życia działania.

Gdy użytkownik zaczyna opuszczać aktywność, system wywołuje metody, aby ją zamknąć. W niektórych przypadkach aktywność jest tylko częściowo demontowana i nadal pozostaje w pamięci, np. gdy użytkownik przełącza się na inną aplikację. W takich przypadkach aktywność może nadal powrócić na pierwszy plan.

Jeśli użytkownik wróci do aktywności, zostanie ona wznowiona od miejsca, w którym została przerwana. Z kilkoma wyjątkami aplikacje mają ograniczone możliwości rozpoczynania aktywności, gdy działają w tle.

Prawdopodobieństwo, że system zakończy dany proces wraz z zawartymi w nim aktywnościami, zależy od stanu aktywności w danym momencie. Więcej informacji o związku między stanem a podatnością na usunięcie znajdziesz w sekcji poświęconej stanowi aktywności i usuwaniu z pamięci.

W zależności od złożoności aktywności prawdopodobnie nie musisz implementować wszystkich metod cyklu życia. Warto jednak poznać wszystkie te metody i wdrożyć te, które sprawią, że aplikacja będzie działać zgodnie z oczekiwaniami użytkowników.

Compose i cykl życia

W Compose unikaj umieszczania logiki biznesowej lub ręcznej konfiguracji obserwatora bezpośrednio w wywołaniach zwrotnych aktywności, takich jak onStart lub onResume. Zamiast tego używaj efektów uwzględniających cykl życia i obserwatorów uwzględniających stan, które automatycznie dostosowują się do obecności interfejsu na ekranie.

  • Kolekcja uwzględniająca cykl życia: użyj collectAsStateWithLifecycle, aby wykorzystać przepływy z ViewModel. Ten interfejs API automatycznie rozpoczyna zbieranie danych, gdy interfejs użytkownika przechodzi w stan uruchomienia, i zatrzymuje je, gdy przechodzi w tło, co zapobiega niepotrzebnemu zużyciu zasobów. Po zebraniu przepływu jako stanu możesz użyć LifecycleEffects, aby uruchomić kod, gdy wystąpi zdarzenie cyklu życia.
  • Przepływ logiki: dzięki tym interfejsom API interfejs użytkownika reaguje na stan cyklu życia w naturalny sposób za pomocą drzewa kompozycji, co gwarantuje, że logika biznesowa jest wykonywana tylko wtedy, gdy użytkownik aktywnie wchodzi w interakcję z komponentem.

Więcej informacji o Compose i cyklu życia znajdziesz w artykule Cykl życia w Jetpack Compose.

Wywołania zwrotne cyklu życia

Ta sekcja zawiera informacje o koncepcji i implementacji metod wywołania zwrotnego używanych podczas cyklu życia działania.

Niektóre działania należą do metod cyklu życia aktywności. Kod, który implementuje działania komponentu zależnego, umieść jednak w komponencie, a nie w metodzie cyklu życia aktywności. Aby to zrobić, musisz sprawić, aby komponent zależny był świadomy cyklu życia. Aby dowiedzieć się, jak sprawić, by komponenty zależne uwzględniały cykl życia, przeczytaj artykuł Cykl życia w Jetpack Compose.

onCreate

Musisz zaimplementować to wywołanie zwrotne, które jest uruchamiane, gdy system po raz pierwszy tworzy aktywność. Po utworzeniu aktywność przechodzi w stan Utworzona. W metodzie onCreate wykonaj podstawową logikę uruchamiania aplikacji, która występuje tylko raz w całym cyklu życia aktywności.

Na przykład implementacja metody onCreate może wiązać dane z listami, powiązać aktywność z ViewModel i utworzyć instancje niektórych zmiennych zakresu klasy. Ta metoda otrzymuje parametr savedInstanceState, który jest obiektem Bundle zawierającym wcześniej zapisany stan aktywności. Jeśli aktywność nigdy wcześniej nie istniała, wartość obiektu Bundle jest równa null.

Jeśli masz komponent uwzględniający cykl życia, który jest powiązany z cyklem życia aktywności, otrzyma on zdarzenie ON_CREATE. Wywoływana jest metoda oznaczona adnotacją @OnLifecycleEvent, dzięki czemu komponent uwzględniający cykl życia może wykonać kod konfiguracji potrzebny do utworzenia stanu.

Poniższy przykład pokazuje, jak zintegrować element kompozycyjny Text w minimalnej aktywności:

class ExampleActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContent { // In here, we can call composables!
            MaterialTheme {
                Greeting(name = "compose")
            }
        }
    }
}

@Composable
fun Greeting(name: String) {
    Text(text = "Hello $name!")
}

Aktywność nie pozostaje w stanie Utworzono. Po zakończeniu wykonywania metody onCreate aktywność przechodzi w stan Uruchomiona, a system szybko wywołuje metody onStart i onResume.

onStart

Gdy aktywność przechodzi w stan Started, system wywołuje metodę onStart. To wywołanie sprawia, że aktywność jest widoczna dla użytkownika, gdy aplikacja przygotowuje się do przeniesienia aktywności na pierwszy plan i umożliwienia interakcji. Na przykład w tej metodzie inicjowany jest kod, który obsługuje interfejs.

Gdy aktywność przechodzi do stanu Rozpoczęta, każdy komponent uwzględniający cykl życia powiązany z cyklem życia aktywności otrzymuje zdarzenie ON_START.

Metoda onStart szybko się kończy i podobnie jak w przypadku stanu Created aktywność nie pozostaje w stanie Started. Po zakończeniu tego wywołania zwrotnego aktywność przechodzi w stan Wznowiono, a system wywołuje metodę onResume.

onResume

Gdy aktywność przechodzi w stan Wznowiono, pojawia się na pierwszym planie, a system wywołuje wywołanie zwrotne onResume. W tym stanie aplikacja wchodzi w interakcję z użytkownikiem. Aplikacja pozostaje w tym stanie, dopóki nie wydarzy się coś, co odwróci od niej uwagę, np. urządzenie odbierze połączenie telefoniczne, użytkownik przejdzie do innego działania lub ekran urządzenia się wyłączy.

Gdy aktywność przechodzi do stanu Wznowiono, każdy komponent uwzględniający cykl życia powiązany z cyklem życia aktywności otrzymuje zdarzenie ON_RESUME. W tym miejscu komponenty cyklu życia mogą włączyć dowolną funkcję, która musi działać, gdy komponent jest widoczny i znajduje się na pierwszym planie, np. uruchomić podgląd z aparatu.

Gdy wystąpi zdarzenie przerywające, aktywność przechodzi w stan Wstrzymana, a system wywołuje wywołanie zwrotne onPause.

Jeśli aktywność powróci ze stanu wstrzymania do stanu wznowienia, system ponownie wywoła metodę onResume. Z tego powodu zaimplementuj metodę onResume, aby inicjować komponenty, które zwalniasz podczas onPause, i wykonywać inne inicjalizacje, które muszą nastąpić za każdym razem, gdy aktywność przechodzi w stan Wznowiono.

Oto przykład komponentu uwzględniającego cykl życia, który uzyskuje dostęp do aparatu, gdy otrzyma zdarzenie ON_RESUME:

class CameraComponent : LifecycleObserver {
    ...
    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
    fun initializeCamera() {
        if (camera == null) {
            getCamera()
        }
    }
    ...
}

Powyższy kod inicjuje kamerę, gdy LifecycleObserver otrzyma zdarzenie ON_RESUME. W trybie wielu okien aktywność może być jednak w pełni widoczna, nawet gdy jest wstrzymana. Na przykład gdy aplikacja jest w trybie wielu okien, a użytkownik kliknie okno, które nie zawiera Twojej aktywności, aktywność przechodzi w stan wstrzymania.

Jeśli chcesz, aby kamera była aktywna tylko wtedy, gdy aplikacja jest wznowiona (widoczna i aktywna na pierwszym planie), zainicjuj kamerę po zdarzeniu ON_RESUME, które zostało pokazane wcześniej. Jeśli chcesz, aby aparat był aktywny, gdy aktywność jest wstrzymana, ale widoczna, np. w trybie wielu okien, zainicjuj aparat po zdarzeniu ON_START.

Jeśli jednak kamera będzie aktywna, gdy aktywność zostanie wstrzymana, może to uniemożliwić dostęp do kamery innej wznowionej aplikacji w trybie wielu okien. Czasami konieczne jest utrzymanie aktywności kamery, gdy aktywność jest wstrzymana, ale może to pogorszyć ogólne wrażenia użytkownika.

Z tego powodu należy dokładnie przemyśleć, w którym momencie cyklu życia najlepiej jest przejąć kontrolę nad współdzielonymi zasobami systemowymi w kontekście trybu wielu okien. Więcej informacji o obsłudze trybu wielu okien znajdziesz w artykule Obsługa trybu wielu okien.

Niezależnie od tego, które zdarzenie przygotowawcze wybierzesz do wykonania operacji inicjowania, pamiętaj, aby użyć odpowiedniego zdarzenia cyklu życia do zwolnienia zasobu. Jeśli inicjujesz coś po zdarzeniu ON_START, zwolnij lub zakończ to po zdarzeniu ON_STOP. Jeśli inicjujesz po zdarzeniu ON_RESUME, zwolnij po zdarzeniu ON_PAUSE.

Poprzedni fragment kodu umieszcza kod inicjowania kamery w komponencie uwzględniającym cykl życia. Możesz zamiast tego umieścić ten kod bezpośrednio w wywołaniach zwrotnych cyklu życia działania, takich jak onStart i onStop, ale nie zalecamy tego rozwiązania. Dodanie tej logiki do niezależnego komponentu uwzględniającego cykl życia pozwala na ponowne użycie komponentu w wielu aktywnościach bez konieczności duplikowania kodu. Aby dowiedzieć się, jak utworzyć komponent uwzględniający cykl życia, przeczytaj artykuł Cykl życia w Jetpack Compose.

onPause

System wywołuje tę metodę jako pierwsze wskazanie, że użytkownik opuszcza aktywność, ale nie zawsze oznacza to, że aktywność jest niszczona. Wskazuje, że aktywność nie jest już na pierwszym planie, ale nadal jest widoczna, jeśli użytkownik jest w trybie wielu okien. Istnieje kilka powodów, dla których aktywność może wejść w ten stan:

  • Zdarzenie, które przerywa działanie aplikacji, zgodnie z opisem w sekcji dotyczącej wywołania zwrotnego onResume, wstrzymuje bieżącą aktywność. To najczęstszy przypadek.
  • W trybie wielu okien tylko jedna aplikacja jest aktywna, a system wstrzymuje wszystkie pozostałe.
  • Otwarcie nowej, półprzezroczystej aktywności, np. okna, wstrzymuje aktywność, którą ona zakrywa. Dopóki aktywność jest częściowo widoczna, ale nie jest aktywna, pozostaje wstrzymana.

Gdy aktywność przechodzi w stan wstrzymania, każdy komponent uwzględniający cykl życia powiązany z cyklem życia aktywności otrzymuje zdarzenie ON_PAUSE. W tym miejscu komponenty cyklu życia mogą zatrzymać wszystkie funkcje, które nie muszą działać, gdy komponent nie jest na pierwszym planie, np. zatrzymać podgląd z kamery.

Użyj metody onPause, aby wstrzymać lub dostosować operacje, których nie można kontynuować lub które mogą być kontynuowane w ograniczonym zakresie, gdy Activity jest w stanie wstrzymania, i które mają zostać wkrótce wznowione.

Możesz też użyć metody onPause, aby zwolnić zasoby systemowe, uchwyty do czujników (np. GPS) lub inne zasoby, które wpływają na czas pracy na baterii, gdy aktywność jest wstrzymana, a użytkownik nie potrzebuje tych zasobów.

Jednak, jak wspomniano w sekcji o onResume, wstrzymana aktywność może być nadal w pełni widoczna, jeśli aplikacja jest w trybie wielu okien. Rozważ użycie onStop zamiast onPause, aby w pełni zwolnić lub dostosować zasoby i operacje związane z interfejsem i lepiej obsługiwać tryb wielu okien.

Poniższy przykład elementu LifecycleObserver reagującego na zdarzenie ON_PAUSE jest odpowiednikiem poprzedniego przykładu zdarzenia ON_RESUME, który zwalnia aparat inicjowany po odebraniu zdarzenia ON_RESUME:

class CameraComponent : LifecycleObserver {
    ...
    @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
    fun releaseCamera() {
        camera?.release()
        camera = null
    }
    ...
}

W tym przykładzie kod zwalniający aparat jest umieszczany po odebraniu zdarzenia ON_PAUSE przez LifecycleObserver.

Wykonywanie funkcji onPause jest bardzo krótkie i niekoniecznie zapewnia wystarczająco dużo czasu na wykonanie operacji zapisu. Dlatego nie używaj onPause do zapisywania danych aplikacji lub użytkownika, wykonywania połączeń sieciowych ani przeprowadzania transakcji w bazie danych. Takie zadanie może nie zostać ukończone przed zakończeniem metody.

Zamiast tego wykonuj operacje wyłączania przy dużym obciążeniu podczas onStop. Więcej informacji o operacjach, które można wykonywać podczas onStop, znajdziesz w następnej sekcji. Więcej informacji o zapisywaniu danych znajdziesz w sekcji dotyczącej zapisywania i przywracania stanu.

Zakończenie metody onPause nie oznacza, że aktywność opuszcza stan wstrzymania. Aktywność pozostaje w tym stanie, dopóki nie zostanie wznowiona lub nie stanie się całkowicie niewidoczna dla użytkownika. Jeśli aktywność zostanie wznowiona, system ponownie wywoła wywołanie zwrotne onResume.

Jeśli aktywność powróci ze stanu wstrzymania do stanu wznowienia, system zachowa instancję Activity w pamięci i przywoła ją, gdy wywoła onResume. W takim przypadku nie musisz ponownie inicjować komponentów utworzonych podczas wywoływania metod wywołania zwrotnego, które prowadzą do stanu wznowienia. Jeśli aktywność stanie się całkowicie niewidoczna, system wywoła metodę onStop.

onStop

Gdy aktywność nie jest już widoczna dla użytkownika, przechodzi w stan Zatrzymana, a system wywołuje wywołanie zwrotne onStop. Może się tak zdarzyć, gdy nowo uruchomiona aktywność zajmuje cały ekran. System wywołuje też metodę onStop, gdy aktywność kończy działanie i ma zostać zakończona.

Gdy aktywność przechodzi w stan Zatrzymana, każdy komponent uwzględniający cykl życia powiązany z cyklem życia aktywności otrzymuje zdarzenie ON_STOP. W tym miejscu komponenty cyklu życia mogą zatrzymać wszystkie funkcje, które nie muszą działać, gdy komponent nie jest widoczny na ekranie.

W metodzie onStop zwolnij lub dostosuj zasoby, które nie są potrzebne, gdy aplikacja nie jest widoczna dla użytkownika. Aplikacja może na przykład wstrzymać animacje lub przełączyć się z dokładnych na przybliżone informacje o zmianie lokalizacji. Użycie onStop zamiast onPause oznacza, że zadania związane z interfejsem są kontynuowane, nawet gdy użytkownik wyświetla aktywność w trybie wielu okien.

Używaj też onStop do wykonywania operacji wyłączania, które wymagają stosunkowo dużej mocy procesora. Jeśli na przykład nie możesz znaleźć lepszego czasu na zapisanie informacji w bazie danych, możesz to zrobić podczas onStop. Poniższy przykład pokazuje implementację metody onStop, która zapisuje zawartość wersji roboczej notatki w pamięci trwałej:

override fun onStop() {
    super.onStop()

    // Delegate the save operation to the ViewModel, which handles the
    // background thread operations (e.g., using Kotlin Coroutines and Room).
    noteViewModel.saveDraft()
}

Gdy aktywność przechodzi w stan Zatrzymana, obiekt Activity jest przechowywany w pamięci: zachowuje wszystkie informacje o stanie i elementach, ale nie jest dołączony do menedżera okien. Gdy aktywność zostanie wznowiona, te informacje zostaną przywołane.

Ze stanu zatrzymania aktywność może powrócić do interakcji z użytkownikiem lub zakończyć działanie i zniknąć. Jeśli aktywność zostanie wznowiona, system wywoła metodę onRestart. Jeśli Activity zakończy działanie, system wywołuje onDestroy.

onDestroy

Metoda onDestroy jest wywoływana przed zniszczeniem aktywności. System wywołuje to wywołanie zwrotne z jednego z dwóch powodów:

  1. Aktywność kończy się, ponieważ użytkownik całkowicie ją odrzucił lub wywołano metodę finish.
  2. System tymczasowo niszczy aktywność z powodu zmiany konfiguracji, np. obrotu urządzenia lub przejścia w tryb wielu okien.

Gdy aktywność przechodzi w stan zniszczenia, każdy komponent uwzględniający cykl życia powiązany z cyklem życia aktywności otrzymuje zdarzenie ON_DESTROY. W tym miejscu komponenty cyklu życia mogą wyczyścić wszystko, co jest potrzebne, zanim Activity zostanie zniszczony.

Zamiast umieszczać w Activity logikę, która określa, dlaczego jest niszczony, użyj obiektu ViewModel, aby zawierał odpowiednie dane widoku dla Activity. Jeśli Activity zostanie utworzony ponownie z powodu zmiany konfiguracji, ViewModel nie musi nic robić, ponieważ jest zachowywany i przekazywany do następnej instancji Activity.

Jeśli Activity nie zostanie utworzony ponownie, w ViewModel zostanie wywołana metoda onCleared, która może wyczyścić wszystkie dane, zanim zostanie zniszczona. Możesz rozróżnić te 2 scenariusze za pomocą metody isFinishing.

Jeśli aktywność się kończy, onDestroy jest ostatnim wywołaniem zwrotnym cyklu życia, które otrzymuje aktywność. Jeśli metoda onDestroy jest wywoływana w wyniku zmiany konfiguracji, system natychmiast tworzy nową instancję aktywności, a następnie wywołuje metodę onCreate w tej nowej instancji w nowej konfiguracji.

Wywołanie zwrotne onDestroy zwalnia wszystkie zasoby, które nie zostały zwolnione przez wcześniejsze wywołania zwrotne, takie jak onStop.

Stan aktywności i usuwanie z pamięci

System kończy procesy, gdy musi zwolnić pamięć RAM. Prawdopodobieństwo, że system zakończy dany proces, zależy od stanu procesu w danym momencie. Stan procesu zależy z kolei od stanu aktywności uruchomionej w procesie. Tabela 1 przedstawia korelacje między stanem procesu, stanem aktywności i prawdopodobieństwem zakończenia procesu przez system. Ta tabela ma zastosowanie tylko wtedy, gdy proces nie uruchamia innych typów komponentów aplikacji.

Prawdopodobieństwo śmierci

Stan procesu

Stan końcowy aktywności

Najniższy

Pierwszy plan (ma lub zaraz będzie mieć fokus)

Wznowiono

Niski

Widoczne (bez zaznaczenia)

Rozpoczęto/wstrzymano

Wyższe

Tło (niewidoczne)

Zatrzymano

Najwyższy

Pusta

Zniszczono

Tabela 1. Związek między cyklem życia procesu a stanem aktywności.

System nigdy nie kończy aktywności bezpośrednio, aby zwolnić pamięć. Zamiast tego kończy proces, w którym działa aktywność, niszcząc nie tylko aktywność, ale też wszystko inne, co działa w tym procesie. Aby dowiedzieć się, jak zachować i przywrócić stan interfejsu aktywności, gdy nastąpi śmierć procesu zainicjowana przez system, zapoznaj się z sekcją dotyczącą zapisywania i przywracania stanu.

Użytkownik może też zakończyć proces, używając Menedżera aplikacji w Ustawieniach, aby zakończyć działanie odpowiedniej aplikacji.

Więcej informacji o procesach znajdziesz w omówieniu procesów i wątków.

Zapisywanie i przywracanie przejściowego stanu interfejsu

Użytkownik oczekuje, że stan interfejsu aktywności pozostanie taki sam podczas zmiany konfiguracji, np. obracania lub przełączania w tryb wielu okien. Jednak gdy nastąpi taka zmiana konfiguracji, system domyślnie niszczy aktywność, usuwając stan interfejsu zapisany w instancji aktywności.

Podobnie użytkownik oczekuje, że stan interfejsu pozostanie taki sam, jeśli tymczasowo przełączy się z Twojej aplikacji na inną, a potem do niej wróci. System może jednak zniszczyć proces aplikacji, gdy użytkownik jest nieobecny, a aktywność jest zatrzymana.

Gdy ograniczenia systemowe niszczą aktywność, zachowaj tymczasowy stan interfejsu użytkownika, korzystając z kombinacji ViewModel (w przypadku złożonej logiki biznesowej i stanu ekranu), interfejsu API Jetpack Compose rememberSaveable (w przypadku lekkiego stanu interfejsu) lub pamięci lokalnej. Więcej informacji o oczekiwaniach użytkowników w porównaniu z zachowaniem systemu oraz o tym, jak najlepiej zachować złożone dane o stanie interfejsu w przypadku aktywności zainicjowanej przez system i śmierci procesu, znajdziesz w artykule Zapisywanie stanów interfejsu.

rememberSaveable automatycznie przetrwa zarówno zmiany konfiguracji, jak i śmierć procesu zainicjowaną przez system, ponieważ stan jest powiązany z kodem, co zapewnia płynne działanie bez konieczności stosowania powtarzalnego kodu na poziomie aktywności.

Stan instancji

Istnieje kilka scenariuszy, w których aktywność jest niszczona w wyniku normalnego działania aplikacji, np. gdy użytkownik naciśnie przycisk Wstecz lub aktywność sygnalizuje własne zniszczenie przez wywołanie metody finish.

Gdy aktywność zostanie zniszczona, ponieważ użytkownik naciśnie przycisk Wstecz lub aktywność zakończy się sama, zarówno system, jak i użytkownik utracą na zawsze dostęp do tej instancji Activity. W takich przypadkach oczekiwania użytkownika są zgodne z działaniem systemu i nie musisz niczego dodatkowo robić.

Jeśli jednak system zniszczy aktywność z powodu ograniczeń systemowych (takich jak zmiana konfiguracji lub obciążenie pamięci), to mimo że rzeczywista instancja Activity zniknie, system zapamięta, że istniała. Jeśli użytkownik spróbuje wrócić do aktywności, system utworzy jej nowe wystąpienie, używając zestawu zapisanych danych, które opisują stan aktywności w momencie jej zniszczenia.

Zapisane dane, których system używa do przywrócenia poprzedniego stanu, nazywają się stanem instancji. W praktyce jest to zbiór par klucz-wartość. Domyślnie system używa stanu instancji do zapisywania podstawowych informacji o układzie interfejsu, takich jak tekst wpisany przez użytkownika czy pozycje przewijania.

Możesz podłączyć się do tego zachowania systemu za pomocą rememberSaveable. Jeśli instancja aktywności zostanie zniszczona i utworzona ponownie, każdy stan interfejsu opakowany w rememberSaveable zostanie automatycznie przywrócony bez konieczności wprowadzania dodatkowego kodu na poziomie aktywności.

Aktywność będzie jednak prawdopodobnie zawierać bardziej złożone informacje o stanie, które chcesz przywrócić, takie jak dane użytkownika, odpowiedzi sieciowe czy zmienne składowe śledzące postępy użytkownika. Mechanizm stanu instancji (a tym samym rememberSaveable) nie nadaje się do przechowywania większej ilości danych, ponieważ wymaga serializacji w głównym wątku i zużywa pamięć procesu systemowego.

Aby zachować więcej niż niewielką ilość danych, zastosuj podejście łączone, używając trwałej pamięci lokalnej, klasy ViewModel i przenoszenia stanu w Compose, zgodnie z opisem w artykule Zapisywanie stanów interfejsu.

Zapisywanie prostego, lekkiego stanu interfejsu za pomocą rememberSaveable

Gdy aktywność zaczyna się zatrzymywać, system przygotowuje się do zapisania informacji o stanie w pakiecie stanu instancji. Aby podłączyć się do tego zachowania systemu, użyj rememberSaveable bezpośrednio w funkcjach typu „composable”. rememberSaveable automatycznie zapisuje i przywraca tymczasowy stan interfejsu, np. wprowadzony przez użytkownika tekst lub pozycje przewijania, podczas ponownego tworzenia aktywności.

Aby zapisać niestandardowe, lekkie informacje o stanie (np. postępy użytkownika w grze), zadeklaruj stan za pomocą rememberSaveable. Platforma Compose obsługuje serializację do pakietu stanu instancji w tle:

var userTypedQuery by rememberSaveable(typedQuery, stateSaver = TextFieldValue.Saver) {
    mutableStateOf(
        TextFieldValue(text = typedQuery, selection = TextRange(typedQuery.length))
    )
}

Aby zapisać dane trwałe, takie jak preferencje użytkownika lub dane do bazy danych, wykorzystaj odpowiednie momenty, gdy aktywność jest na pierwszym planie. Jeśli nie ma takiej możliwości, zapisz trwałe dane podczas metody onStop.

Przywracanie stanu interfejsu aktywności za pomocą zapisanego stanu instancji

Gdy aktywność zostanie odtworzona po wcześniejszym zniszczeniu, przywracanie stanu następuje automatycznie. Gdy używasz rememberSaveable, nie musisz pisać żadnej jawnej logiki przywracania, sprawdzać, czy pakiety są puste, ani zastępować wywołań zwrotnych aktywności. Kod, który inicjuje i zapisuje stan, płynnie go przywraca, gdy aktywność zostaje wznowiona:

var userTypedQuery by rememberSaveable(typedQuery, stateSaver = TextFieldValue.Saver) {
    mutableStateOf(
        TextFieldValue(text = typedQuery, selection = TextRange(typedQuery.length))
    )
}

Aktywności i nawigacja

Aplikacja prawdopodobnie będzie wielokrotnie przechodzić między ekranami, na przykład gdy użytkownik kliknie przycisk Wstecz na urządzeniu lub wybierze nowe miejsce docelowe. Nowoczesne aplikacje na Androida zwykle używają architektury z jedną aktywnością. Zamiast uruchamiać nową aktywność Activity dla każdego ekranu, aplikacja hostuje pojedynczą aktywność Activity i używa komponentu Navigation do zamiany ekranów kompozycyjnych w ramach tej aktywności.

Aby dowiedzieć się, jak wdrożyć nowoczesną nawigację opartą na Compose, zapoznaj się z przewodnikiem po bibliotece Navigation 3 w Jetpack Compose.

Uruchamianie jednej aktywności z poziomu innej

W pewnym momencie aktywność może wymagać rozpoczęcia innej aktywności. Taka potrzeba pojawia się na przykład wtedy, gdy aplikacja musi przejść z bieżącego ekranu na nowy.

W zależności od tego, czy aktywność ma otrzymać wynik z nowej aktywności, którą ma rozpocząć, uruchamiasz nową aktywność za pomocą metody startActivity lub startActivityForResult. W obu przypadkach przekazujesz obiekt Intent.

Obiekt Intent określa dokładną aktywność, którą chcesz rozpocząć, lub opisuje typ działania, które chcesz wykonać. System wybierze dla Ciebie odpowiednią aktywność, która może pochodzić nawet z innej aplikacji. Obiekt Intent może też zawierać niewielkie ilości danych, które zostaną użyte przez uruchomioną aktywność. Więcej informacji o klasie Intent znajdziesz w artykule Intencje i filtry intencji.

startActivity

Jeśli nowo rozpoczęta aktywność nie musi zwracać wyniku, bieżąca aktywność może ją uruchomić, wywołując metodę startActivity.

Podczas pracy we własnej aplikacji często wystarczy po prostu uruchomić znaną aktywność. Na przykład ten fragment kodu pokazuje, jak uruchomić aktywność o nazwie SignInActivity.

val context = LocalContext.current

Button(onClick = {
    val intent = Intent(context, SignInActivity::class.java)
    context.startActivity(intent)
}) {
    Text("Sign In")
}

Rozpoczynanie aktywności zewnętrznych

Nawigacja wewnątrz aplikacji jest obsługiwana przez Navigation, ale Activity będzie czasami musiał uruchamiać inne aktywności. Zwykle dzieje się tak, gdy chcesz wykorzystać zewnętrzną aplikację do wykonania określonego działania, takiego jak otwarcie przeglądarki, wysłanie e-maila lub zrobienie zdjęcia.

Aby to zrobić, użyj obiektu Intent, który opisuje typ działania, które chcesz wykonać, a system uruchomi odpowiednią aktywność z innej aplikacji.

Jeśli na przykład chcesz umożliwić użytkownikowi wysłanie e-maila, możesz utworzyć taką intencję:

val intent = Intent(Intent.ACTION_SEND).apply {
    putExtra(Intent.EXTRA_EMAIL, recipientArray)
}
startActivity(intent)

Jeśli chcesz uruchomić aktywność zewnętrzną i uzyskać wynik (np. poprosić aplikację aparatu o zrobienie zdjęcia i zwrócenie obrazu), użyj nowoczesnych interfejsów API wyników Activity zamiast wycofanego wywołania zwrotnego startActivityForResult.

Koordynowanie działań

Gdy jedna aktywność uruchamia inną, obie przechodzą przez cykl życia. Pierwsza aktywność przestaje działać i przechodzi w stan wstrzymania lub zatrzymania, a druga aktywność jest tworzona. Jeśli te aktywności udostępniają dane zapisane na dysku lub w innym miejscu, pamiętaj, że pierwsza aktywność nie zostanie całkowicie zatrzymana, zanim zostanie utworzona druga. Zamiast tego proces uruchamiania drugiej maszyny wirtualnej nakłada się na proces zatrzymywania pierwszej.

Kolejność wywołań zwrotnych cyklu życia jest dobrze zdefiniowana, zwłaszcza gdy 2 aktywności są w tym samym procesie (czyli w tej samej aplikacji), a jedna uruchamia drugą. Oto kolejność operacji, które są wykonywane, gdy aktywność A uruchamia aktywność B:

  1. Zostanie wykonana metoda onPause aktywności A.
  2. Metody onCreate, onStart i onResume aktywności B są wykonywane po kolei. Aktywność B jest teraz aktywna.
  3. Jeśli aktywność A nie jest już widoczna na ekranie, wykonywana jest jej metoda onStop.

Ta sekwencja wywołań zwrotnych cyklu życia umożliwia zarządzanie przejściem informacji z jednej aktywności do drugiej.

Dodatkowe materiały

Więcej informacji o cyklu życia działania znajdziesz w tych dodatkowych materiałach:

Wyświetlanie treści