Obsługa cykli życia dzięki komponentom uwzględniającym cykl życia Zawiera Android Jetpack.
Komponenty uwzględniające cykl życia podejmują działania w odpowiedzi na zmianę stanu cyklu życia innego komponentu, np. działań i fragmentów. Te pozwalają tworzyć lepiej zorganizowany i często prostszy kod, jest łatwiejszy w utrzymaniu.
Popularnym wzorcem jest implementacja działań zależnych komponentów w funkcji metod cyklu życia działań i fragmentów. Ten schemat prowadzi jednak do niewłaściwa organizacja kodu i rozprzestrzenianie się błędów. Za pomocą uwzględniających cykl życia, możesz przenieść kod powiązanych komponentów w metodach cyklu życia i w samych komponentach.
androidx.lifecycle
udostępnia klasy i interfejsy, które pozwalają przystosowywać się do cyklu życia.
czyli komponentów, które automatycznie dostosowują swoje
na podstawie bieżącego stanu cyklu życia działania lub fragmentu.
Większość komponentów aplikacji zdefiniowanych w Android Framework powiązane z nimi cykle życia. Cyklami życia zarządza system operacyjny lub z kodem platformy działającym w Twoim procesie. Są kluczowe dla działania Androida a aplikacja musi je respektować. Jeśli tego nie zrobisz, mogą wystąpić wycieki pamięci lub nawet awarii aplikacji.
Wyobraź sobie, że mamy aktywność, która pokazuje lokalizację urządzenia na ekranie. O typowa implementacja może być taka:
Kotlin
internal class MyLocationListener( private val context: Context, private val callback: (Location) -> Unit ) { fun start() { // connect to system location service } fun stop() { // disconnect from system location service } } class MyActivity : AppCompatActivity() { private lateinit var myLocationListener: MyLocationListener override fun onCreate(...) { myLocationListener = MyLocationListener(this) { location -> // update UI } } public override fun onStart() { super.onStart() myLocationListener.start() // manage other components that need to respond // to the activity lifecycle } public override fun onStop() { super.onStop() myLocationListener.stop() // manage other components that need to respond // to the activity lifecycle } }
Java
class MyLocationListener { public MyLocationListener(Context context, Callback callback) { // ... } void start() { // connect to system location service } void stop() { // disconnect from system location service } } class MyActivity extends AppCompatActivity { private MyLocationListener myLocationListener; @Override public void onCreate(...) { myLocationListener = new MyLocationListener(this, (location) -> { // update UI }); } @Override public void onStart() { super.onStart(); myLocationListener.start(); // manage other components that need to respond // to the activity lifecycle } @Override public void onStop() { super.onStop(); myLocationListener.stop(); // manage other components that need to respond // to the activity lifecycle } }
Mimo że ten fragment wygląda dobrze, w prawdziwej aplikacji pojawia się za dużo
wywołania, które zarządzają interfejsem użytkownika i innymi komponentami w odpowiedzi na bieżący stan
w kontekście ich cyklu życia. Zarządzanie wieloma komponentami niesie ze sobą sporą część
w metodach cyklu życia, takich jak onStart()
czy
onStop()
, co utrudnia ich utrzymanie.
Co więcej, nie ma gwarancji, że komponent rozpocznie się przed działaniem lub
zatrzymano fragment. Dotyczy to szczególnie sytuacji, gdy musimy
długo trwającą operację, taką jak sprawdzenie konfiguracji w onStart()
. Może to spowodować warunek wyścigu, w którym metoda onStop()
zakończy działanie przed onStart()
, przez co komponent będzie żył dłużej niż trwa
niezbędną.
Kotlin
class MyActivity : AppCompatActivity() { private lateinit var myLocationListener: MyLocationListener override fun onCreate(...) { myLocationListener = MyLocationListener(this) { location -> // update UI } } public override fun onStart() { super.onStart() Util.checkUserStatus { result -> // what if this callback is invoked AFTER activity is stopped? if (result) { myLocationListener.start() } } } public override fun onStop() { super.onStop() myLocationListener.stop() } }
Java
class MyActivity extends AppCompatActivity { private MyLocationListener myLocationListener; public void onCreate(...) { myLocationListener = new MyLocationListener(this, location -> { // update UI }); } @Override public void onStart() { super.onStart(); Util.checkUserStatus(result -> { // what if this callback is invoked AFTER activity is stopped? if (result) { myLocationListener.start(); } }); } @Override public void onStop() { super.onStop(); myLocationListener.stop(); } }
androidx.lifecycle
udostępnia klasy i interfejsy, które ułatwiają rozwiązywanie tych problemów
wytrzymały i izolowany sposób.
Cykl życia
Lifecycle
to zajęcia
który zawiera informacje o stanie cyklu życia komponentu (np.
działania lub fragmentu) i pozwala innym obiektom obserwować ten stan.
Lifecycle
używa 2 głównych
wyliczenia, aby śledzić stan cyklu życia powiązanego z nim komponentu:
- Wydarzenie
- Zdarzenia cyklu życia wysyłane z platformy i platformy
Zajęcia:
Lifecycle
. Te są mapowane na zdarzenia wywołania zwrotnego w aktywnościach i fragmentach. - Region
- Bieżący stan komponentu śledzonego przez
Lifecycle
.
Pomyśl o stanach jako o węzłach grafu, a zdarzeniach – jako krawędziach między dla tych węzłów.
Klasa może monitorować stan cyklu życia komponentu, implementując
DefaultLifecycleObserver
i zastępowanie odpowiednich metod, takich jak onCreate
, onStart
itp.
Następnie możesz dodać obserwatora, wywołując funkcję
addObserver()
.
Lifecycle
i przekazywanie instancji obserwatora, jak w przykładzie poniżej
przykład:
Kotlin
class MyObserver : DefaultLifecycleObserver { override fun onResume(owner: LifecycleOwner) { connect() } override fun onPause(owner: LifecycleOwner) { disconnect() } } myLifecycleOwner.getLifecycle().addObserver(MyObserver())
Java
public class MyObserver implements DefaultLifecycleObserver { @Override public void onResume(LifecycleOwner owner) { connect() } @Override public void onPause(LifecycleOwner owner) { disconnect() } } myLifecycleOwner.getLifecycle().addObserver(new MyObserver());
W powyższym przykładzie obiekt myLifecycleOwner
implementuje funkcję
LifecycleOwner
.
i został objaśniony w następnej sekcji.
LifecycleOwner
LifecycleOwner
to
interfejsu jednej metody, który wskazuje, że klasa ma
Lifecycle
Zawiera jedną
,
getLifecycle()
który musi zostać wdrożony przez klasę.
Jeśli próbujesz zarządzać cyklem życia całej aplikacji
Więcej informacji znajdziesz w artykule
ProcessLifecycleOwner
Ten interfejs przedstawia abstrakcyjne prawa własności
Lifecycle
od osoby fizycznej
takich jak Fragment
i AppCompatActivity
, oraz umożliwia pisanie komponentów, które
z nimi współpracować. Każda niestandardowa klasa aplikacji może implementować
LifecycleOwner
za pomocą prostego interfejsu online.
Komponenty, które implementują
DefaultLifecycleObserver
współdziałają z komponentami, które stosują
LifecycleOwner
ponieważ właściciel może określić cykl życia, w którym obserwator może się zarejestrować.
zegarka.
W przypadku przykładu śledzenia lokalizacji możemy utworzyć klasę MyLocationListener
zastosuj DefaultLifecycleObserver
a następnie zainicjuj ją kodem aktywności
Lifecycle
w metodzie onCreate()
. Dzięki temu funkcja
MyLocationListener
, co oznacza, że logika
w reakcji na zmiany stanu cyklu życia jest zadeklarowany w zasadzie MyLocationListener
.
danej aktywności. To, że poszczególne komponenty przechowują własną logikę,
łatwiej jest zarządzać logiką działań i fragmentów.
Kotlin
class MyActivity : AppCompatActivity() { private lateinit var myLocationListener: MyLocationListener override fun onCreate(...) { myLocationListener = MyLocationListener(this, lifecycle) { location -> // update UI } Util.checkUserStatus { result -> if (result) { myLocationListener.enable() } } } }
Java
class MyActivity extends AppCompatActivity { private MyLocationListener myLocationListener; public void onCreate(...) { myLocationListener = new MyLocationListener(this, getLifecycle(), location -> { // update UI }); Util.checkUserStatus(result -> { if (result) { myLocationListener.enable(); } }); } }
Typowym przypadkiem użycia jest unikanie wywoływania określonych wywołań zwrotnych, jeśli
Lifecycle
nie jest w dobrym
w tym stanie. Jeśli na przykład wywołanie zwrotne uruchamia transakcję dotyczącą fragmentu po
zapis stanu aktywności spowodowałoby wypadek, więc nigdy
aby wywołać to wywołanie zwrotne.
Aby ułatwić ten przypadek użycia,
Lifecycle
klasa zezwala
lub inne obiekty, aby wysłać zapytanie dotyczące bieżącego stanu.
Kotlin
internal class MyLocationListener( private val context: Context, private val lifecycle: Lifecycle, private val callback: (Location) -> Unit ): DefaultLifecycleObserver { private var enabled = false override fun onStart(owner: LifecycleOwner) { if (enabled) { // connect } } fun enable() { enabled = true if (lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) { // connect if not connected } } override fun onStop(owner: LifecycleOwner) { // disconnect if connected } }
Java
class MyLocationListener implements DefaultLifecycleObserver { private boolean enabled = false; public MyLocationListener(Context context, Lifecycle lifecycle, Callback callback) { ... } @Override public void onStart(LifecycleOwner owner) { if (enabled) { // connect } } public void enable() { enabled = true; if (lifecycle.getCurrentState().isAtLeast(STARTED)) { // connect if not connected } } @Override public void onStop(LifecycleOwner owner) { // disconnect if connected } }
Przy tej implementacji nasza klasa LocationListener
jest całkowicie
z uwzględnieniem cyklu życia. Jeśli musimy użyć karty LocationListener
w innej aktywności
lub fragmentu, wystarczy go zainicjować. Cała konfiguracja i wymiana
Operacje są zarządzane przez samą klasę.
Jeśli biblioteka udostępnia zajęcia, które muszą być dostosowane do cyklu życia Androida, zalecamy korzystanie z komponentów uwzględniających cykl życia. Twoi bibliotekarze mogą łatwo zintegrować te komponenty bez ręcznego zarządzania cyklem życia po stronie klienta.
Implementowanie niestandardowego właściciela cyklu życia
Fragmenty i działania w bibliotece pomocy w wersji 26.1.0 i nowszych są już implementowane
LifecycleOwner
za pomocą prostego interfejsu online.
Jeśli masz klasę niestandardową, w której chcesz utworzyć
LifecycleOwner
, Ty
może użyć funkcji
Rejestr cyklu życia
, ale musisz przekazać do nich zdarzenia, jak pokazano poniżej
przykładowy kod:
Kotlin
class MyActivity : Activity(), LifecycleOwner { private lateinit var lifecycleRegistry: LifecycleRegistry override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) lifecycleRegistry = LifecycleRegistry(this) lifecycleRegistry.markState(Lifecycle.State.CREATED) } public override fun onStart() { super.onStart() lifecycleRegistry.markState(Lifecycle.State.STARTED) } override fun getLifecycle(): Lifecycle { return lifecycleRegistry } }
Java
public class MyActivity extends Activity implements LifecycleOwner { private LifecycleRegistry lifecycleRegistry; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); lifecycleRegistry = new LifecycleRegistry(this); lifecycleRegistry.markState(Lifecycle.State.CREATED); } @Override public void onStart() { super.onStart(); lifecycleRegistry.markState(Lifecycle.State.STARTED); } @NonNull @Override public Lifecycle getLifecycle() { return lifecycleRegistry; } }
Sprawdzone metody dotyczące komponentów uwzględniających cykl życia
- Kontrolery interfejsu (działania i fragmenty) powinny być jak najbardziej oszczędne. Ta
nie powinni próbować pozyskiwać własnych danych; zamiast tego użyj
ViewModel
, aby to zrobić, i obserwujLiveData
w celu odzwierciedlenia zmian w widokach. - Spróbuj napisać interfejsy oparte na danych, w których kontroler UI odpowiada za
aktualizować widoki w miarę zmian danych lub powiadamiać użytkowników o działaniach
ViewModel
- Uwzględnij logikę w zakresie danych w
ViewModel
. AplikacjaViewModel
powinna wyświetlać reklamy jako połączenie między kontrolerem UI a resztą aplikacji. Bądź jednak nie jest tak użytkownikaViewModel
za pobieranie danych (na przykład z sieci). Zamiast tego: AplikacjaViewModel
powinna wywołać odpowiedni komponent do pobrania danych, a potem przekaż wynik z powrotem do za pomocą kontrolera UI. - Użyj funkcji Data Binding, aby utrzymać przejrzysty interfejs między widokami a kontrolerem UI. Dzięki temu możesz: zwiększ deklaratowość i zminimalizuj kod aktualizacji, zapisywać w swoich działaniach i fragmentach. Jeśli wolisz to zrobić w Javie języka programowania, użyj biblioteki takiej jak Nóż obudowy – unikanie powtarzalnych elementów i mają większą abstrakcję.
- Jeśli Twój interfejs jest złożony, rozważ utworzenie prowadzący do obsługi modyfikacji interfejsu użytkownika. Może to być pracochłonne, ale ułatwiają testowanie komponentów interfejsu.
- Unikaj odniesień do:
View
lubActivity
wViewModel
. Jeśli aktywnośćViewModel
przestaje być aktywna (w przypadku zmiany konfiguracji), dane o aktywności wyciekają i nie są odpowiednio usuwane przez śmieci. - Do zarządzania można używać współrzędnych Kotlin długotrwałe zadania i inne operacje, które mogą być uruchamiane asynchronicznie.
Przypadki użycia komponentów uwzględniających cykl życia
Komponenty uwzględniające cykl życia mogą znacznie ułatwić zarządzanie cyklami życia w różnych przypadkach. Oto kilka przykładów:
- Przełączanie między przybliżoną i szczegółową aktualizacją lokalizacji. Używaj
z uwzględnieniem cyklu życia komponentów, aby umożliwić szczegółową aktualizację lokalizacji podczas
aplikacja do określania lokalizacji jest widoczna i przełącza się na bardziej szczegółową aktualizację, gdy aplikacja jest
w tle.
LiveData
, komponent uwzględniający cykl życia, umożliwia aplikacji automatyczne aktualizowanie interfejsu użytkownika po zmianie użytkownika lokalizacji. - Zatrzymanie i rozpoczęcie buforowania filmu. Zacznij od komponentów dopasowanych do cyklu życia buforowanie filmu tak szybko, jak to możliwe, z opóźnieniem odtwarzania do momentu, gdy aplikacja będzie całkowicie rozpoczęto. Aby zakończyć buforowanie, możesz też użyć komponentów uwzględniających cykl życia gdy Twoja aplikacja zostanie zniszczona.
- Zaczynam i zatrzymuję połączenie sieciowe. Wykorzystaj komponenty uwzględniające cykl życia, aby: włącz aktualizowanie na żywo (strumieniowe) danych sieciowych, gdy aplikacja jest na pierwszym planie, a także automatycznie wstrzymywać działanie aplikacji, w tle.
- Wstrzymywanie i wznawianie animowanych elementów możliwych do rysowania. Wykorzystaj komponenty uwzględniające cykl życia, aby: uchwyt wstrzymywania animowanych elementów rysunkowych, gdy aplikacja działa w tle, wznawiania elementów rysowanych po uruchomieniu aplikacji na pierwszym planie.
Obsługa w przypadku zdarzeń zatrzymania
Gdy Lifecycle
należy do AppCompatActivity
lub Fragment
, Lifecycle
stan zmienia się na
CREATED
i
ON_STOP
jest wysyłane, gdy AppCompatActivity
lub
Fragment
onSaveInstanceState()
.
Gdy stan urządzenia Fragment
lub AppCompatActivity
jest zapisywany w
onSaveInstanceState()
, to UI
jest uważany za niezmienny do
ON_START
jest
. Próba zmodyfikowania interfejsu użytkownika po zapisaniu stanu prawdopodobnie spowoduje
niespójności w stanie nawigacji aplikacji, dlatego FragmentManager
zgłasza wyjątek, jeśli aplikacja uruchomi
FragmentTransaction
po zapisaniu stanu. Zobacz
commit()
, aby wyświetlić szczegóły.
LiveData
sprawia, że ten przypadek nie jest już gotowy, ponieważ
przed wywołaniem obserwatora, jeśli powiązany z obserwatorem jest Lifecycle
nie jest przynajmniej
STARTED
.
Materiały zza kulis wzywają
isAtLeast()
przed podjęciem decyzji o wywołaniu jego obserwatora.
Metoda onStop()
metody AppCompatActivity
jest wywoływana po
onSaveInstanceState()
,
co zostawia lukę, w której zmiany stanu UI są niedozwolone, ale
Aplikacja Lifecycle
nie została jeszcze przeniesiona do
CREATED
stanu.
Aby zapobiec temu problemowi, klasa Lifecycle
w wersji beta2
i zmniejsz oznaczenie stanu jako
CREATED
bez wysyłania zdarzenia, dzięki czemu kod sprawdzający bieżący
otrzymuje rzeczywistą wartość, mimo że zdarzenie nie jest wysyłane do czasu wywołania funkcji onStop()
przez system.
Niestety, to rozwiązanie ma dwa główne problemy:
- Na poziomie API 23 i niższym system Android zapisuje stan
nawet jeśli jest ona częściowo objęta inną aktywnością. W innym
słów, system Android nazywa
onSaveInstanceState()
ale niekoniecznie wywołujeonStop()
. Potencjalnie to długi przedział czasu, w którym obserwator nadal uważa, że cykl życia jest aktywny chociaż nie można zmienić jego stanu UI. - Każda klasa, która chce udostępnić podobne zachowanie
Klasa
LiveData
musi wdrożyć obejście opisane przezLifecycle
w wersjibeta 2
i starszych.
Dodatkowe materiały
Aby dowiedzieć się więcej o obsłudze cykli życia za pomocą komponentów uwzględniających cykl życia, zapoznaj się z tymi dodatkowymi materiałami.
Próbki
- Przykład podstawowych komponentów architektury Androida
- Słonecznik, czyli aplikację w wersji demonstracyjnej ilustrującą sprawdzone metody Komponenty architektury
Ćwiczenia z programowania
Blogi
.Polecane dla Ciebie
- Uwaga: tekst linku wyświetla się, gdy JavaScript jest wyłączony
- Omówienie LiveData
- Używanie współprogramów Kotlin z komponentami dostosowanymi do cyklu życia
- Moduł Saved State dla modelu ViewModel