Pojęcia i implementacja w Jetpack Compose
Gdy użytkownik przechodzi między ekranami w aplikacji, opuszcza ją i do niej wraca, instancje Activity
w aplikacji przechodzą przez różne stany w swoim cyklu życia.
Klasa Activity udostępnia wiele 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 Twoja aktywność ma się zachowywać, gdy użytkownik opuści ją i do niej wróci. Jeśli na przykład tworzysz odtwarzacz strumieniowy, możesz wstrzymać odtwarzanie filmu 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 w tym samym miejscu.
Każde wywołanie zwrotne umożliwia wykonanie określonej pracy odpowiedniej do danego stanu. Wykonywanie odpowiednich działań we właściwym czasie i prawidłowe obsługiwanie przejść sprawiają, że aplikacja jest bardziej niezawodna i wydajna. Na przykład prawidłowe wdrożenie wywołań zwrotnych cyklu życia może pomóc aplikacji uniknąć tych problemów:
- zawieszać się, gdy użytkownik odbiera połączenie telefoniczne lub przełącza się na inną aplikację podczas korzystania z Twojej aplikacji;
- Zużywanie cennych zasobów systemowych, gdy użytkownik nie korzysta aktywnie z urządzenia.
- Utrata postępów użytkownika, jeśli opuści on aplikację i wróci do niej później.
- Aplikacja ulega awarii lub użytkownik traci postępy, gdy ekran obraca się między orientacją poziomą a pionową.
W tym dokumencie szczegółowo opisujemy cykl życia aktywności. Dokument zaczyna się od opisu paradygmatu cyklu życia. Następnie wyjaśniamy, co dzieje się w przypadku każdego wywołania zwrotnego, co musisz w jego ramach wdrożyć i jakie są jego wewnętrzne mechanizmy.
Następnie krótko opisuje związek między stanem działania a podatnością procesu na zamknięcie przez system. Na koniec omówimy 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 Obsługa cykli życia za pomocą komponentów uwzględniających cykl życia i Zapisywanie stanów interfejsu. Aby dowiedzieć się, jak zaprojektować niezawodną aplikację o jakości produkcyjnej za pomocą aktywności w połączeniu z komponentami architektury, przeczytaj przewodnik po architekturze aplikacji.
Pojęcia związane z cyklem życia działania
Aby poruszać się między stanami cyklu życia aktywności, klasa Activity udostępnia podstawowy zestaw 6 wywołań zwrotnych: onCreate, onStart, onResume, onPause, onStop i onDestroy. System wywołuje każdą z tych funkcji zwrotnych, gdy aktywność przechodzi w nowy stan.
Rysunek 1 przedstawia wizualizację tego paradygmatu.
Gdy użytkownik zaczyna opuszczać aktywność, system wywołuje metody, aby ją zamknąć. W niektórych przypadkach działanie jest tylko częściowo zamykane i nadal znajduje się w pamięci, np. gdy użytkownik przełącza się na inną aplikację. W takich przypadkach działanie może nadal powrócić na pierwszy plan.
Jeśli użytkownik wróci do działania, zostanie ono wznowione w miejscu, w którym zostało przerwane. Z kilkoma wyjątkami aplikacje nie mogą rozpoczynać działań, gdy są uruchomione w tle.
Prawdopodobieństwo, że system zakończy dany proces wraz z aktywnościami w nim zawartymi, zależy od stanu aktywności w danym momencie. Więcej informacji o zależności między stanem działania a podatnością na usunięcie znajdziesz w sekcji Stan działania i usuwanie 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ć każdą z nich i wdrożyć te, które sprawią, że aplikacja będzie działać zgodnie z oczekiwaniami użytkowników.
Wywołania zwrotne cyklu życia
Ta sekcja zawiera informacje koncepcyjne i wdrożeniowe dotyczące metod wywołania zwrotnego używanych w cyklu życia aktywności.
Niektóre działania należą do metod cyklu życia aktywności. Umieść jednak kod, który implementuje działania komponentu zależnego, w komponencie, a nie w metodzie cyklu życia działania. Aby to osiągnąć, musisz sprawić, że komponent zależny będzie uwzględniać cykl życia. Aby dowiedzieć się, jak sprawić, by komponenty zależne były świadome cyklu życia, przeczytaj artykuł Obsługa cykli życia za pomocą komponentów świadomych cyklu życia.
onCreate
Musisz zaimplementować to wywołanie zwrotne, które jest wywoływane, gdy system po raz pierwszy tworzy aktywność. Po utworzeniu aktywność przechodzi w stan Utworzono. W metodzie onCreate wykonaj podstawową logikę uruchamiania aplikacji, która jest wykonywana tylko raz w całym cyklu życia aktywności.
Na przykład implementacja onCreate może wiązać dane z listami, przypisywać aktywność do ViewModel i tworzyć instancje niektórych zmiennych w zakresie 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ść Bundle
obiektu to null.
Jeśli masz komponent uwzględniający cykl życia, który jest powiązany z cyklem życia aktywności, otrzymuje on zdarzenie ON_CREATE. Metoda oznaczona adnotacją @OnLifecycleEvent jest wywoływana, aby komponent uwzględniający cykl życia mógł wykonać kod konfiguracji potrzebny w stanie utworzonym.
Poniższy przykład metody onCreate pokazuje podstawową konfigurację aktywności, np. deklarowanie interfejsu użytkownika (zdefiniowanego w pliku układu XML), definiowanie zmiennych składowych i konfigurowanie niektórych elementów interfejsu. W tym przykładzie plik układu XML przekazuje identyfikator zasobu pliku R.layout.main_activity do setContentView.
Kotlin
lateinit var textView: TextView
// Some transient state for the activity instance.
var gameState: String? = null
override fun onCreate(savedInstanceState: Bundle?) {
// Call the superclass onCreate to complete the creation of
// the activity, like the view hierarchy.
super.onCreate(savedInstanceState)
// Recover the instance state.
gameState = savedInstanceState?.getString(GAME_STATE_KEY)
// Set the user interface layout for this activity.
// The layout is defined in the project res/layout/main_activity.xml file.
setContentView(R.layout.main_activity)
// Initialize member TextView so it is available later.
textView = findViewById(R.id.text_view)
}
// This callback is called only when there is a saved instance previously saved using
// onSaveInstanceState(). Some state is restored in onCreate(). Other state can optionally
// be restored here, possibly usable after onStart() has completed.
// The savedInstanceState Bundle is same as the one used in onCreate().
override fun onRestoreInstanceState(savedInstanceState: Bundle?) {
textView.text = savedInstanceState?.getString(TEXT_VIEW_KEY)
}
// Invoked when the activity might be temporarily destroyed; save the instance state here.
override fun onSaveInstanceState(outState: Bundle?) {
outState?.run {
putString(GAME_STATE_KEY, gameState)
putString(TEXT_VIEW_KEY, textView.text.toString())
}
// Call superclass to save any view hierarchy.
super.onSaveInstanceState(outState)
}
Java
TextView textView;
// Some transient state for the activity instance.
String gameState;
@Override
public void onCreate(Bundle savedInstanceState) {
// Call the superclass onCreate to complete the creation of
// the activity, like the view hierarchy.
super.onCreate(savedInstanceState);
// Recover the instance state.
if (savedInstanceState != null) {
gameState = savedInstanceState.getString(GAME_STATE_KEY);
}
// Set the user interface layout for this activity.
// The layout is defined in the project res/layout/main_activity.xml file.
setContentView(R.layout.main_activity);
// Initialize member TextView so it is available later.
textView = (TextView) findViewById(R.id.text_view);
}
// This callback is called only when there is a saved instance previously saved using
// onSaveInstanceState(). Some state is restored in onCreate(). Other state can optionally
// be restored here, possibly usable after onStart() has completed.
// The savedInstanceState Bundle is same as the one used in onCreate().
@Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
textView.setText(savedInstanceState.getString(TEXT_VIEW_KEY));
}
// Invoked when the activity might be temporarily destroyed; save the instance state here.
@Override
public void onSaveInstanceState(Bundle outState) {
outState.putString(GAME_STATE_KEY, gameState);
outState.putString(TEXT_VIEW_KEY, textView.getText());
// Call superclass to save any view hierarchy.
super.onSaveInstanceState(outState);
}
Zamiast definiować plik XML i przekazywać go do funkcji setContentView, możesz utworzyć nowe obiekty View w kodzie aktywności i zbudować hierarchię widoków, wstawiając nowe obiekty View do obiektu ViewGroup. Następnie używasz tego układu, przekazując element główny ViewGroup do funkcji setContentView. Więcej informacji o tworzeniu interfejsu znajdziesz w dokumentacji interfejsu.
Aktywność nie pozostaje w stanie Utworzono. Po zakończeniu wykonywania metody onCreate aktywność przechodzi w stan Started, a system wywołuje kolejno metody onStart i onResume.
onStart
Gdy aktywność przejdzie w stan Started (Rozpoczęto), system wywoła funkcję onStart.
To wywołanie sprawia, że aktywność jest widoczna dla użytkownika, gdy aplikacja przygotowuje się do przejścia aktywności na pierwszy plan i umożliwienia interakcji z nią. Na przykład w tej metodzie inicjowany jest kod, który obsługuje interfejs.
Gdy aktywność przejdzie w stan Started, każdy komponent uwzględniający cykl życia powiązany z cyklem życia aktywności otrzyma zdarzenie ON_START.
Metoda onStart kończy się szybko i podobnie jak w przypadku stanu Created aktywność nie pozostaje w stanie Started. Po zakończeniu tego wywołania zwrotnego aktywność przechodzi w stan Resumed, a system wywołuje metodę onResume.
onResume
Gdy aktywność przechodzi w stan wznowienia, pojawia się na pierwszym planie, a system wywołuje wywołanie zwrotne onResume. Jest to stan, w którym aplikacja wchodzi w interakcję z użytkownikiem. Aplikacja pozostaje w tym stanie, dopóki coś nie odwróci uwagi od aplikacji, np. gdy urządzenie odbierze połączenie telefoniczne, użytkownik przejdzie do innej aktywności lub ekran urządzenia się wyłączy.
Gdy aktywność przejdzie w stan wznowienia, każdy komponent powiązany z cyklem życia aktywności otrzyma zdarzenie ON_RESUME. W tym miejscu komponenty cyklu życia mogą włączać dowolne funkcje, które muszą działać, gdy komponent jest widoczny i na pierwszym planie, np. uruchamiać podgląd z kamery.
Gdy wystąpi zdarzenie przerywające, aktywność przechodzi w stan Wstrzymana, a system wywołuje wywołanie zwrotne onPause.
Jeśli aktywność powróci do stanu wznowienia ze stanu wstrzymania, system ponownie wywoła metodę onResume. Z tego powodu zaimplementuj metodę onResume, aby zainicjować komponenty zwalniane podczas wywołania metody onPause i wykonać inne inicjowanie, które musi nastąpić za każdym razem, gdy aktywność przechodzi w stan wznowienia.
Oto przykład komponentu uwzględniającego cykl życia, który uzyskuje dostęp do kamery, gdy otrzyma zdarzenie ON_RESUME:
Kotlin
class CameraComponent : LifecycleObserver {
...
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
fun initializeCamera() {
if (camera == null) {
getCamera()
}
}
...
}
Java
public class CameraComponent implements LifecycleObserver {
...
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
public void initializeCamera() {
if (camera == null) {
getCamera();
}
}
...
}
Powyższy kod inicjuje kamerę, gdy LifecycleObserver otrzyma zdarzenie ON_RESUME. W trybie wielu okien Twoja aktywność może być jednak w pełni widoczna nawet wtedy, 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, przechodzi ona w stan wstrzymania.
Jeśli chcesz, aby kamera była aktywna tylko wtedy, gdy aplikacja jest wznawiana (widoczna i aktywna na pierwszym planie), zainicjuj ją po zdarzeniu ON_RESUME, które zostało opisane wcześniej. Jeśli chcesz, aby kamera była aktywna, gdy aktywność jest wstrzymana, ale widoczna, np. w trybie wielu okien, zainicjuj kamerę po zdarzeniu ON_START.
Jednak włączenie kamery, gdy aktywność jest wstrzymana, może 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 dokładnie przemyśl, w którym momencie cyklu życia najbardziej odpowiednie jest przejęcie kontroli nad udostępnionymi 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, w którym zdarzeniu przygotowawczym wykonasz operację inicjowania, pamiętaj, aby użyć odpowiedniego zdarzenia cyklu życia do zwolnienia zasobu. Jeśli zainicjujesz 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.
Powyższy fragment kodu umieszcza kod inicjowania kamery w komponencie uwzględniającym cykl życia. Zamiast tego możesz umieścić ten kod bezpośrednio w wywołaniach zwrotnych cyklu życia działania, takich jak onStart i onStop, ale nie zalecamy tego. Dodanie tej logiki do niezależnego komponentu uwzględniającego cykl życia umożliwia 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ł Obsługa cykli życia za pomocą komponentów uwzględniających cykl życia (Widoki).
onPause
System wywołuje tę metodę jako pierwszy sygnał, że użytkownik opuszcza aktywność, chociaż nie zawsze oznacza to, że aktywność jest niszczona. Oznacza to, że aktywność nie jest już na pierwszym planie, ale nadal jest widoczna, jeśli użytkownik korzysta z trybu wielu okien. Aktywność może przejść w ten stan z kilku powodów:
- 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 dialogowego, wstrzymuje aktywność, którą zasłania. Dopóki aktywność jest częściowo widoczna, ale nie jest aktywna, pozostaje wstrzymana.
Gdy aktywność przechodzi w stan Wstrzymana, każdy komponent powiązany z cyklem życia aktywności otrzymuje zdarzenie ON_PAUSE. W tym miejscu komponenty cyklu życia mogą zatrzymać każdą funkcję, która nie musi 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óre nie mogą być kontynuowane lub mogą być kontynuowane w ograniczonym zakresie, gdy Activity jest w stanie Wstrzymane, 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 żywotność baterii, gdy aktywność jest wstrzymana, a użytkownik ich nie potrzebuje.
Jednak, jak wspomniano w sekcji dotyczącej onResume, wstrzymana aktywność może być nadal w pełni widoczna, jeśli aplikacja działa w trybie wielu okien. Zamiast onPause używaj onStop, aby w pełni zwalniać lub dostosowywać zasoby i operacje związane z interfejsem, co pozwoli lepiej obsługiwać tryb wielu okien.
Poniższy przykład LifecycleObserver reagującego na zdarzenie ON_PAUSE jest odpowiednikiem poprzedniego przykładu zdarzenia ON_RESUME. Zwalnia on kamerę, która inicjuje się po otrzymaniu zdarzenia ON_RESUME:
Kotlin
class CameraComponent : LifecycleObserver {
...
@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
fun releaseCamera() {
camera?.release()
camera = null
}
...
}
Java
public class JavaCameraComponent implements LifecycleObserver {
...
@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
public void releaseCamera() {
if (camera != null) {
camera.release();
camera = null;
}
}
...
}
W tym przykładzie kod zwalniający migawkę aparatu jest umieszczony po odebraniu zdarzenia ON_PAUSE przez LifecycleObserver.
onPause trwa bardzo krótko i nie zawsze daje wystarczająco dużo czasu na wykonanie operacji zapisywania. Z tego powodu nie używaj onPause do zapisywania danych aplikacji lub danych użytkownika, wykonywania połączeń sieciowych ani przeprowadzania transakcji w bazie danych.
Takie działanie może nie zostać ukończone przed zakończeniem metody.
Zamiast tego wykonuj operacje zamykania przy dużym obciążeniu w czasie onStop. Więcej informacji o odpowiednich operacjach do wykonania podczas onStop znajdziesz w następnej sekcji. Więcej informacji o zapisywaniu danych znajdziesz w sekcji zapisywanie i przywracanie stanu.
Zakończenie działania metody onPause nie oznacza, że aktywność przestanie być w stanie 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 Wstrzymana do stanu Wznowiona, system zachowa instancję Activity w pamięci i przywoła ją, gdy wywoła Activity.onResume W takim przypadku nie musisz ponownie inicjować komponentów utworzonych w trakcie żadnej z metod wywołania zwrotnego prowadzących do stanu wznowienia. Jeśli aktywność stanie się całkowicie niewidoczna, system wywoła onStop.
onStop
Gdy aktywność przestaje być widoczna dla użytkownika, przechodzi w stan Zatrzymano, a system wywołuje wywołanie zwrotne onStop. Może się to zdarzyć, gdy nowo uruchomiona aktywność zajmuje cały ekran. System wywołuje też onStop, gdy aktywność kończy działanie i ma zostać zakończona.
Gdy aktywność przejdzie w stan Stopped, każdy komponent powiązany z cyklem życia aktywności otrzyma zdarzenie ON_STOP. W tym miejscu komponenty cyklu życia mogą zatrzymać każdą funkcję, która nie musi działać, gdy komponent nie jest widoczny na ekranie.
W onStop zwalniaj lub dostosowuj zasoby, które nie są potrzebne, gdy aplikacja jest niewidoczna dla użytkownika. Na przykład aplikacja może wstrzymać animacje lub przełączyć się z dokładnych na przybliżone aktualizacje lokalizacji. Użycie onStopzamiast onPause oznacza, że praca związana z interfejsem użytkownika jest kontynuowana, nawet gdy użytkownik wyświetla Twoją aktywność w trybie wielu okien.
Używaj też onStop do wykonywania stosunkowo wymagających operacji zamykania. Jeśli na przykład nie możesz znaleźć lepszego czasu na zapisanie informacji w bazie danych, możesz to zrobić w trakcie onStop. W przykładzie poniżej pokazujemy implementację funkcji
onStop, która zapisuje zawartość notatki w wersji roboczej w pamięci trwałej:
Kotlin
override fun onStop() {
// Call the superclass method first.
super.onStop()
// Save the note's current draft, because the activity is stopping
// and we want to be sure the current note progress isn't lost.
val values = ContentValues().apply {
put(NotePad.Notes.COLUMN_NAME_NOTE, getCurrentNoteText())
put(NotePad.Notes.COLUMN_NAME_TITLE, getCurrentNoteTitle())
}
// Do this update in background on an AsyncQueryHandler or equivalent.
asyncQueryHandler.startUpdate(
token, // int token to correlate calls
null, // cookie, not used here
uri, // The URI for the note to update.
values, // The map of column names and new values to apply to them.
null, // No SELECT criteria are used.
null // No WHERE columns are used.
)
}
Java
@Override
protected void onStop() {
// Call the superclass method first.
super.onStop();
// Save the note's current draft, because the activity is stopping
// and we want to be sure the current note progress isn't lost.
ContentValues values = new ContentValues();
values.put(NotePad.Notes.COLUMN_NAME_NOTE, getCurrentNoteText());
values.put(NotePad.Notes.COLUMN_NAME_TITLE, getCurrentNoteTitle());
// Do this update in background on an AsyncQueryHandler or equivalent.
asyncQueryHandler.startUpdate (
mToken, // int token to correlate calls
null, // cookie, not used here
uri, // The URI for the note to update.
values, // The map of column names and new values to apply to them.
null, // No SELECT criteria are used.
null // No WHERE columns are used.
);
}
W powyższym przykładowym kodzie używana jest bezpośrednio baza danych SQLite. Zalecamy jednak używanie biblioteki trwałości danych Room, która zapewnia warstwę abstrakcji nad SQLite. Więcej informacji o zaletach korzystania z biblioteki Room i o tym, jak ją wdrożyć w aplikacji, znajdziesz w przewodniku Biblioteka trwałości danych Room.
Gdy aktywność przechodzi w stan zatrzymania, obiekt Activity jest przechowywany w pamięci: zachowuje wszystkie informacje o stanie i elementach, ale nie jest powiązany z menedżerem okien. Gdy aktywność zostanie wznowiona, te informacje zostaną przywrócone.
Nie musisz ponownie inicjować komponentów utworzonych w trakcie żadnej z metod wywołania zwrotnego prowadzących do stanu wznowienia. System śledzi też bieżący stan każdego obiektu View w układzie, więc jeśli użytkownik wpisze tekst w widżecie EditText, treść zostanie zachowana i nie musisz jej zapisywać ani przywracać.
Gdy aktywność jest w stanie zatrzymania, może wrócić do interakcji z użytkownikiem lub zakończyć działanie i zostać zamknięta. Jeśli aktywność zostanie przywrócona, system wywoła metodę onRestart. Jeśli Activity zakończy działanie, system wywoła onDestroy.
onDestroy
onDestroy jest wywoływana przed zniszczeniem aktywności. System wywołuje ten wywołanie zwrotne z jednego z 2 powodów:
- Aktywność dobiega końca, ponieważ użytkownik całkowicie ją odrzucił lub wywołano w niej metodę
finish. - System tymczasowo niszczy aktywność z powodu zmiany konfiguracji, np. obrócenia urządzenia lub przejścia do trybu 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ą zwalniać miejsce, zanim Activity zostanie zniszczony.
Zamiast umieszczać w Activity logikę określającą, dlaczego jest on 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 ponownie utworzony, wywoływana jest metoda onCleared, która umożliwia zwolnienie miejsca na wszelkie potrzebne dane przed zniszczeniem.ViewModel Te 2 sytuacje możesz odróżnić za pomocą metody isFinishing.
Jeśli aktywność się kończy, onDestroy jest ostatnim wywołaniem zwrotnym cyklu życia, które otrzymuje aktywność. Jeśli wywołanie funkcji onDestroy nastąpi w wyniku zmiany konfiguracji, system natychmiast utworzy nową instancję aktywności, a następnie wywoła w niej funkcję onCreate w nowej konfiguracji.
Wywołanie zwrotne onDestroy zwalnia wszystkie zasoby, które nie zostały zwolnione przez wcześniejsze wywołania zwrotne, np. onStop.
Zapisywanie i przywracanie przejściowego stanu interfejsu
Użytkownik oczekuje, że stan interfejsu aktywności pozostanie taki sam podczas zmiany konfiguracji, np. obrócenia ekranu lub przejścia do trybu wielu okien. Gdy jednak nastąpi taka zmiana konfiguracji, system domyślnie niszczy aktywność, usuwając wszelkie stany interfejsu przechowywane w jej instancji.
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 systemu powodują zniszczenie aktywności, zachowaj przejściowy stan interfejsu użytkownika za pomocą kombinacji ViewModel, onSaveInstanceState lub pamięci lokalnej. Więcej informacji o oczekiwaniach użytkowników w porównaniu z zachowaniem systemu oraz o tym, jak najlepiej zachowywać złożone dane o stanie interfejsu w przypadku aktywności inicjowanej przez system i śmierci procesu, znajdziesz w artykule Zapisywanie stanów interfejsu.
W tej sekcji znajdziesz informacje o stanie instancji i sposobie implementacji metody
onSaveInstance, która jest wywołaniem zwrotnym w samej aktywności. Jeśli dane interfejsu są niewielkie, możesz użyć samego onSaveInstance, aby zachować stan interfejsu zarówno w przypadku zmian konfiguracji, jak i w przypadku śmierci procesu zainicjowanej przez system. Jednak ponieważ onSaveInstance wiąże się z kosztami serializacji i deserializacji, w większości przypadków używasz zarówno ViewModel, jak i onSaveInstance, co opisano w sekcji Zapisywanie stanów interfejsu.
Stan instancji
Istnieje kilka scenariuszy, w których aktywność jest niszczona z powodu normalnego działania aplikacji, np. gdy użytkownik naciśnie przycisk Wstecz lub gdy aktywność sygnalizuje własne zniszczenie przez wywołanie metody finish.
Gdy aktywność zostanie zniszczona, ponieważ użytkownik naciśnie przycisk Wstecz lub aktywność sama się zakończy, zarówno system, jak i użytkownik stracą na zawsze pojęcie o tym wystąpieniu 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 (np. zmiany konfiguracji lub braku pamięci), to chociaż rzeczywista instancja Activityzostanie usunięta, system zapamięta, że istniała. Jeśli użytkownik spróbuje wrócić do aktywności, system utworzy nową instancję tej aktywności, używając zestawu zapisanych danych opisujących stan aktywności w momencie jej zniszczenia.
Zapisane dane, których system używa do przywracania poprzedniego stanu, nazywane są stanem instancji. Jest to zbiór par klucz-wartość przechowywanych w obiekcie Bundle. Domyślnie system używa Bundlestanu instancji do zapisywania informacji o każdym obiekcie View w układzie aktywności, np. wartości tekstowej wpisanej w widżet EditText.
Jeśli więc instancja aktywności zostanie zniszczona i utworzona ponownie, stan układu zostanie przywrócony do poprzedniego stanu bez konieczności pisania kodu. Jednak Twoja aktywność może zawierać więcej informacji o stanie, które chcesz przywrócić, np. zmienne członkowskie śledzące postępy użytkownika w aktywności.
Obiekt Bundle 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ż bardzo małą ilość danych, zastosuj połączone podejście do przechowywania danych, korzystając z trwałego lokalnego miejsca na dane, metody onSaveInstanceState i klasy ViewModel, zgodnie z opisem w artykule Zapisywanie stanów interfejsu.
Zapisywanie prostego, lekkiego stanu interfejsu za pomocą metody onSaveInstanceState
Gdy aktywność zaczyna się zatrzymywać, system wywołuje metodę onSaveInstanceState, aby aktywność mogła zapisać informacje o stanie w pakiecie stanu instancji. Domyślna implementacja tej metody zapisuje przejściowe informacje o stanie hierarchii widoków aktywności, takie jak tekst w widżecie EditText lub pozycja przewijania widżetu ListView.
Aby zapisać dodatkowe informacje o stanie instancji dla aktywności, zastąp metodę
onSaveInstanceState i dodaj pary klucz-wartość do obiektu Bundle, który jest
zapisywany w przypadku nieoczekiwanego zniszczenia aktywności. Jeśli zastąpisz metodę onSaveInstanceState, musisz wywołać implementację klasy nadrzędnej, jeśli chcesz, aby implementacja domyślna zapisała stan hierarchii widoków.
Pokazuje to poniższy przykład:
Kotlin
override fun onSaveInstanceState(outState: Bundle?) {
// Save the user's current game state.
outState?.run {
putInt(STATE_SCORE, currentScore)
putInt(STATE_LEVEL, currentLevel)
}
// Always call the superclass so it can save the view hierarchy state.
super.onSaveInstanceState(outState)
}
companion object {
val STATE_SCORE = "playerScore"
val STATE_LEVEL = "playerLevel"
}
Java
static final String STATE_SCORE = "playerScore";
static final String STATE_LEVEL = "playerLevel";
// ...
@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
// Save the user's current game state.
savedInstanceState.putInt(STATE_SCORE, currentScore);
savedInstanceState.putInt(STATE_LEVEL, currentLevel);
// Always call the superclass so it can save the view hierarchy state.
super.onSaveInstanceState(savedInstanceState);
}
Aby zapisać trwałe dane, takie jak preferencje użytkownika lub dane bazy danych, wykorzystuj odpowiednie momenty, gdy Twoja aktywność jest na pierwszym planie. Jeśli taka możliwość się nie pojawi, zapisz trwałe dane w metodzie onStop.
Przywracanie stanu interfejsu aktywności za pomocą zapisanego stanu instancji
Gdy aktywność zostanie ponownie utworzona po wcześniejszym zniszczeniu, możesz odzyskać zapisany stan instancji z obiektu Bundle, który system przekazuje do aktywności. Zarówno metoda wywołania zwrotnego onCreate, jak i onRestoreInstanceState otrzymują ten sam obiekt Bundle, który zawiera informacje o stanie instancji.
Metoda onCreate jest wywoływana niezależnie od tego, czy system tworzy nową instancję aktywności, czy odtwarza poprzednią. Zanim spróbujesz odczytać stan Bundle, musisz sprawdzić, czy ma on wartość null. Jeśli ma wartość null, oznacza to, że system tworzy nową instancję aktywności zamiast przywracać poprzednią, która została zniszczona.
Poniższy fragment kodu pokazuje, jak przywrócić niektóre dane stanu w onCreate:
Kotlin
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) // Always call the superclass first
// Check whether we're recreating a previously destroyed instance.
if (savedInstanceState != null) {
with(savedInstanceState) {
// Restore value of members from saved state.
currentScore = getInt(STATE_SCORE)
currentLevel = getInt(STATE_LEVEL)
}
} else {
// Probably initialize members with default values for a new instance.
}
// ...
}
Java
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); // Always call the superclass first
// Check whether we're recreating a previously destroyed instance.
if (savedInstanceState != null) {
// Restore value of members from saved state.
currentScore = savedInstanceState.getInt(STATE_SCORE);
currentLevel = savedInstanceState.getInt(STATE_LEVEL);
} else {
// Probably initialize members with default values for a new instance.
}
// ...
}
Zamiast przywracać stan podczas wywołania funkcji onCreate, możesz zaimplementować funkcję onRestoreInstanceState, którą system wywołuje po wywołaniu metody onStart. System wywołuje funkcję onRestoreInstanceState tylko wtedy, gdy jest zapisany stan do przywrócenia, więc nie musisz sprawdzać, czy Bundle ma wartość null.
Kotlin
override fun onRestoreInstanceState(savedInstanceState: Bundle?) {
// Always call the superclass so it can restore the view hierarchy.
super.onRestoreInstanceState(savedInstanceState)
// Restore state members from saved instance.
savedInstanceState?.run {
currentScore = getInt(STATE_SCORE)
currentLevel = getInt(STATE_LEVEL)
}
}
Java
public void onRestoreInstanceState(Bundle savedInstanceState) {
// Always call the superclass so it can restore the view hierarchy.
super.onRestoreInstanceState(savedInstanceState);
// Restore state members from saved instance.
currentScore = savedInstanceState.getInt(STATE_SCORE);
currentLevel = savedInstanceState.getInt(STATE_LEVEL);
}
Przechodzenie między aktywnościami
Aplikacja może wchodzić w stan aktywności i z niego wychodzić, być może wielokrotnie, w trakcie swojego działania, np. gdy użytkownik naciśnie przycisk Wstecz na urządzeniu lub gdy aktywność uruchomi inną aktywność.
W tej sekcji znajdziesz informacje, które pomogą Ci skutecznie wdrożyć przejścia między aktywnościami. Obejmują one m.in. rozpoczynanie aktywności z poziomu innej aktywności, zapisywanie stanu działania i przywracanie stanu działania.
Uruchamianie jednej aktywności z poziomu innej
Działanie często musi w pewnym momencie rozpocząć inne działanie. Jest to potrzebne 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ąć, uruchom 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 wybiera odpowiednią aktywność, która może pochodzić nawet z innej aplikacji. Obiekt Intent może też zawierać niewielkie ilości danych, które będą używane przez uruchomione działanie. 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 w aplikacji często wystarczy uruchomić znaną aktywność. Na przykład poniższy fragment kodu pokazuje, jak uruchomić aktywność o nazwie SignInActivity.
Kotlin
val intent = Intent(this, SignInActivity::class.java)
startActivity(intent)
Java
Intent intent = new Intent(this, SignInActivity.class);
startActivity(intent);
Aplikacja może też wykonywać pewne działania, takie jak wysyłanie e-maili, SMS-ów lub aktualizacji stanu, przy użyciu danych z Twojej aktywności. W takim przypadku aplikacja może nie mieć własnych działań, które umożliwiają wykonanie takich czynności, więc możesz zamiast tego wykorzystać działania udostępniane przez inne aplikacje na urządzeniu, które mogą wykonać te czynności za Ciebie.
W tym przypadku intencje są bardzo przydatne. Możesz utworzyć intencję, która opisuje działanie, jakie chcesz wykonać, a system uruchomi odpowiednią aktywność z innej aplikacji. Jeśli istnieje kilka działań, które mogą obsłużyć intencję, użytkownik może wybrać, które z nich ma zostać użyte. Jeśli na przykład chcesz umożliwić użytkownikowi wysyłanie e-maili, możesz utworzyć ten zamiar:
Kotlin
val intent = Intent(Intent.ACTION_SEND).apply {
putExtra(Intent.EXTRA_EMAIL, recipientArray)
}
startActivity(intent)
Java
Intent intent = new Intent(Intent.ACTION_SEND);
intent.putExtra(Intent.EXTRA_EMAIL, recipientArray);
startActivity(intent);
Dodatkowy element EXTRA_EMAIL dodany do intencji to tablica tekstowa adresów e-mail, na które ma zostać wysłana wiadomość. Gdy aplikacja poczty e-mail odpowie na tę intencję, odczyta tablicę tekstową podaną w dodatkowych informacjach i umieści adresy w polu „Do” formularza tworzenia e-maila. W takiej sytuacji rozpoczyna się działanie aplikacji poczty e-mail, a gdy użytkownik skończy, Twoje działanie zostanie wznowione.
startActivityForResult
Czasami po zakończeniu aktywności chcesz uzyskać wynik. Możesz na przykład rozpocząć działanie, które pozwoli użytkownikowi wybrać osobę z listy kontaktów. Po zakończeniu zwraca wybraną osobę. W tym celu wywołaj metodę startActivityForResult(Intent, int), w której parametr całkowity identyfikuje wywołanie.
Ten identyfikator ma na celu odróżnienie wielu wywołań funkcji startActivityForResult(Intent, int) z tego samego działania. Nie jest to identyfikator globalny i nie ma ryzyka konfliktu z innymi aplikacjami ani działaniami. Wynik jest zwracany za pomocą metody onActivityResult(int, int, Intent).
Gdy aktywność dziecka zostanie zakończona, może wywołać metodę setResult(int), aby zwrócić dane do aktywności nadrzędnej. Działanie podrzędne musi podać kod wyniku, który może być standardowym wynikiem RESULT_CANCELED, RESULT_OK lub dowolną wartością niestandardową zaczynającą się od RESULT_FIRST_USER.
Dodatkowo aktywność dziecka może opcjonalnie zwracać Intent obiekt
zawierający wszelkie dodatkowe dane. Aktywność nadrzędna używa metody
onActivityResult(int, int, Intent) wraz z identyfikatorem liczbowym
pierwotnie dostarczonym przez aktywność nadrzędną, aby otrzymać informacje.
Jeśli aktywność dziecka zakończy się niepowodzeniem z jakiegokolwiek powodu, np. z powodu awarii, aktywność rodzica otrzyma wynik z kodem RESULT_CANCELED.
Kotlin
class MyActivity : Activity() {
// ...
override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
// When the user center presses, let them pick a contact.
startActivityForResult(
Intent(Intent.ACTION_PICK,Uri.parse("content://contacts")),
PICK_CONTACT_REQUEST)
return true
}
return false
}
override fun onActivityResult(requestCode: Int, resultCode: Int, intent: Intent?) {
when (requestCode) {
PICK_CONTACT_REQUEST ->
if (resultCode == RESULT_OK) {
// A contact was picked. Display it to the user.
startActivity(Intent(Intent.ACTION_VIEW, intent?.data))
}
}
}
companion object {
internal val PICK_CONTACT_REQUEST = 0
}
}
Java
public class MyActivity extends Activity {
// ...
static final int PICK_CONTACT_REQUEST = 0;
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
// When the user center presses, let them pick a contact.
startActivityForResult(
new Intent(Intent.ACTION_PICK,
new Uri("content://contacts")),
PICK_CONTACT_REQUEST);
return true;
}
return false;
}
protected void onActivityResult(int requestCode, int resultCode,
Intent data) {
if (requestCode == PICK_CONTACT_REQUEST) {
if (resultCode == RESULT_OK) {
// A contact was picked. Display it to the user.
startActivity(new Intent(Intent.ACTION_VIEW, data));
}
}
}
}
koordynowanie działań,
Gdy jedna aktywność uruchamia drugą, obie przechodzą przez zmiany stanu cyklu życia. Pierwsza aktywność przestaje działać i przechodzi w stan Wstrzymana lub Zatrzymana, a druga aktywność zostaje utworzona. Jeśli te działania współdzielą dane zapisane na dysku lub w innym miejscu, pamiętaj, że pierwsze działanie nie jest całkowicie zatrzymywane przed utworzeniem drugiego. Proces rozpoczynania drugiego projektu nakłada się na proces zatrzymywania pierwszego.
Kolejność wywołań zwrotnych cyklu życia jest dobrze zdefiniowana, zwłaszcza gdy obie aktywności znajdują się w tym samym procesie, czyli w tej samej aplikacji, a jedna z nich uruchamia drugą. Oto kolejność operacji, które występują, gdy aktywność A uruchamia aktywność B:
- Wykonuje się metoda
onPausedziałania A. - Metody
onCreate,onStartionResumedziałania B są wykonywane kolejno. Aktywność B jest teraz aktywna. - 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 jednego działania do drugiego.