LifecycleOwner — to kluczowy interfejs z biblioteki Android Jetpack, który deklaruje, że obiekt posiada cykl życia i udostępnia do niego dostęp poprzez metodę getLifecycle(). Leży u podstaw komponentowej architektury nowoczesnych aplikacji na Androida, pozwalając oddzielić logikę pracy z cyklem życia od konkretnej implementacji Activity lub Fragment. Według danych Google I/O 2024, ponad 85% nowych projektów na Androida używa LifecycleOwner do zarządzania subskrypcjami i zapobiegania wyciekom pamięci. Ten interfejs jest fundamentem dla LiveData, ViewModel i innych komponentów Jetpack, zapewniając bezpieczne wykonywanie kodu tylko w aktywnym stanie komponentu.
Najważniejsze
LifecycleOwner — to interfejs z pakietu androidx.lifecycle, który zawiera jedyną metodę getLifecycle(), zwracającą obiekt Lifecycle. Ten obiekt śledzi bieżący stan komponentu (CREATED, STARTED, RESUMED, DESTROYED) i powiadamia wszystkich subskrybujących obserwatorów o jego zmianie. LifecycleOwner jest częścią Architecture Components i wchodzi w skład biblioteki lifecycle-runtime.
Głównym zadaniem interfejsu jest standaryzacja dostępu do cyklu życia. Przed pojawieniem się Jetpack programiści stosowali ręczną subskrypcję w onStart i wypisywanie w onStop, co prowadziło do duplikacji kodu i błędów. LifecycleOwner rozwiązuje ten problem, udostępniając jednolity mechanizm dla wszystkich komponentów Androida. Zamiast jawnego wywoływania metod cyklu życia programista subskrybuje Lifecycle raz, a powiadomienia przychodzą automatycznie.
Interfejs jest zadeklarowany w Kotlin jako interfejs funkcyjny z jedną abstrakcyjną metodą:
interface LifecycleOwner {
val lifecycle: Lifecycle
}
Dzięki funkcyjnemu charakterowi interfejsu, łatwo go zaimplementować za pomocą delegata lub lambdy. Jest to szczególnie wygodne do tworzenia Custom Views i klas ViewModel, które powinny reagować na zmiany cyklu życia hosta. Otrzymany z getLifecycle() obiekt Lifecycle udostępnia metody addObserver i removeObserver do zarządzania subskrypcjami.
LifecycleOwner działa w połączeniu z dwiema kluczowymi klasami: Lifecycle i LifecycleObserver. Lifecycle przechowuje bieżący stan komponentu w postaci enum State (INITIALIZED, CREATED, STARTED, RESUMED, DESTROYED) i śledzi przejścia między nimi. Gdy stan się zmienia, Lifecycle powiadamia wszystkich zarejestrowanych obserwatorów, wywołując odpowiednie adnotowane metody. Ten mechanizm nazywa się „lifecycle-aware” — kod jest wykonywany tylko wtedy, gdy komponent znajduje się w odpowiednim stanie.
Mechanizm przekazywania zdarzeń opiera się na wzorcu Observer. LifecycleOwner pełni rolę Observable, a implementacja LifecycleObserver — rolę Observer. Activity lub Fragment przy zmianie swojego stanu (onCreate → onStart → onResume → onPause → onStop → onDestroy) powiadamia Lifecycle przez wewnętrzny mechanizm ReportFragment, który jest dodawany do systemu AndroidX automatycznie. Programista nie musi ręcznie wywoływać metod Lifecycle — wszystko dzieje się automatycznie.
| Stan Lifecycle | Zdarzenie | Metoda cyklu życia Androida |
|---|---|---|
| INITIALIZED | — | Przed onCreate |
| CREATED | ON_CREATE | onCreate |
| STARTED | ON_START | onStart |
| RESUMED | ON_RESUME | onResume |
| STARTED | ON_PAUSE | onPause |
| CREATED | ON_STOP | onStop |
| DESTROYED | ON_DESTROY | onDestroy |
Ważny szczegół: Lifecycle gwarantuje, że zdarzenia ON_STOP i ON_DESTROY zostaną dostarczone nawet w przypadku awaryjnego zakończenia procesu. To czyni LifecycleOwner niezawodnym narzędziem do zwalniania krytycznych zasobów. Do zwykłego zapisywania stanu zaleca się używanie SavedStateHandle w ViewModel, ale LifecycleOwner zapewnia podstawowy poziom bezpieczeństwa.
Istnieją dwa sposoby subskrypcji zdarzeń LifecycleOwner: klasyczny LifecycleObserver z adnotacjami i nowoczesny DefaultLifecycleObserver z jawnymi metodami. Drugie podejście jest zalecane przez Google od 2022 roku, ponieważ zapewnia lepsze bezpieczeństwo typów i unika refleksji, która była używana w podejściu adnotacyjnym. DefaultLifecycleObserver wymaga używania Java 8+ lub Kotlin i jest preferowany dla nowych projektów.
Przykład subskrypcji przez DefaultLifecycleObserver:
class MyObserver : DefaultLifecycleObserver {
override fun onStart(owner: LifecycleOwner) {
// Uruchamianie GPS-śledzenia tylko gdy komponent jest aktywny
startLocationUpdates()
}
override fun onStop(owner: LifecycleOwner) {
// Bezpieczne zatrzymanie przy przejściu w tło
stopLocationUpdates()
}
}
// Połączenie:
lifecycleOwner.lifecycle.addObserver(MyObserver())
Każda metoda DefaultLifecycleObserver przyjmuje LifecycleOwner jako parametr. Pozwala to obserwatorowi uzyskać dostęp do kontekstu wykonywanego komponentu bez konieczności przekazywania go osobno. Takie podejście czyni kod bardziej modułowym i testowalnym — Observer nie zależy od konkretnej implementacji Activity lub Fragment, a pracuje z abstrakcją LifecycleOwner.
Stary sposób z użyciem adnotacji @OnLifecycleEvent wciąż występuje w projektach legacy, ale jego używanie nie jest zalecane dla nowego kodu. Refleksja niezbędna do przetwarzania adnotacji dodaje narzut i może prowadzić do błędów, które nie są wykrywane na etapie kompilacji. Google oficjalnie zaleca migrację na DefaultLifecycleObserver.
// Przestarzałe podejście — niezalecane dla nowych projektów
class MyLegacyObserver : LifecycleObserver {
@OnLifecycleEvent(Lifecycle.Event.ON_START)
fun onStart() {
startLocationUpdates()
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
fun onStop() {
stopLocationUpdates()
}
}
Podejście adnotacyjne ma istotną wadę: brak kontroli czasu życia Observer. Jeśli programista zapomni wypisać Observer przy zniszczeniu LifecycleOwner, obiekt Observer pozostanie w pamięci do momentu uruchomienia garbage collectora. DefaultLifecycleObserver rozwiązuje ten problem — Observer jest powiązany z Lifecycle i automatycznie wypisuje się przy przejściu do stanu DESTROYED.
Od wersji AppCompat 1.1.0 i AndroidX Fragment 1.2.0, wszystkie Activity i Fragment dziedziczące po AppCompatActivity lub Fragment automatycznie są LifecycleOwner. Oznacza to, że metoda getLifecycle() jest w nich dostępna domyślnie, a subskrypcja zdarzeń cyklu życia działa bez dodatkowej konfiguracji. Programista musi jedynie wywołać lifecycle.addObserver() z dowolnego miejsca w Activity lub Fragment.
Rozważmy przykład integracji LifecycleOwner w Activity:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
lifecycle.addObserver(LocationObserver(this))
}
}
W tym przykładzie lifecycle to extension property, dostępne dzięki AndroidX Activity. Observer LocationObserver będzie automatycznie otrzymywać powiadomienia o starcie (ON_START) i zatrzymaniu (ON_STOP) Activity. Przy obrocie ekranu Observer otrzymuje powiadomienie o ON_DESTROY, a następnie o ON_CREATE, co pozwala prawidłowo obsługiwać zmiany konfiguracyjne bez dodatkowego kodu.
Fragment implementuje LifecycleOwner przez interfejs, a jego Lifecycle jest powiązany z cyklem życia Fragment, a nie nadrzędnego Activity. To ważne: Lifecycle Fragment przechodzi w DESTROYED, gdy Fragment jest usuwany z transakcji, podczas gdy Activity może pozostawać w RESUMED. Ta różnica pozwala Observer subskrybować oddzielnie cykl życia każdego komponentu.
class MyFragment : Fragment() {
private val uiStateObserver = UiStateObserver()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
lifecycle.addObserver(uiStateObserver)
}
}
Ważną zaletą używania LifecycleOwner we Fragment jest automatyczne wypisywanie przy przejściu Fragment w DESTROYED. Jest to szczególnie istotne w przypadku ViewPager, gdzie Fragment mogą być tworzone i niszczone dynamicznie. Ręczne zarządzanie subskrypcjami w tym scenariuszu byłoby niezwykle skomplikowane i podatne na błędy.
Interfejs LifecycleOwner można zaimplementować w dowolnej klasie, która ma cykl życia. Jest to przydatne dla Custom Views, Service, a nawet ViewModel w niektórych rozwiązaniach architektonicznych. Google udostępnia pomocniczą klasę LifecycleRegistry, która zarządza stanem Lifecycle i generuje zdarzenia. Programista musi ręcznie wywoływać odpowiednie metody LifecycleRegistry przy zmianie stanu komponentu.
Przykład implementacji LifecycleOwner w Custom View:
class MyCustomView(
context: Context,
attrs: AttributeSet?
) : FrameLayout(context, attrs), LifecycleOwner {
private val lifecycleRegistry = LifecycleRegistry(this)
override val lifecycle: Lifecycle
get() = lifecycleRegistry
fun onStart() {
lifecycleRegistry.setCurrentState(Lifecycle.State.STARTED)
}
fun onStop() {
lifecycleRegistry.setCurrentState(Lifecycle.State.CREATED)
}
}
W tym przykładzie LifecycleRegistry pełni rolę magazynu stanu. Metody onStart/onStop powinny być wywoływane przez komponent nadrzędny (np. Activity), gdy Custom View staje się widoczny lub ukrywa się. LifecycleRegistry automatycznie oblicza niezbędne zdarzenia do przejścia między stanami i powiadamia wszystkich subskrybujących Observer.
Przy implementacji własnego LifecycleOwner ważne jest przestrzeganie zasady: stan LifecycleRegistry powinien być aktualizowany jako ostatni w odpowiedniej metodzie cyklu życia, po wszystkich pozostałych operacjach. Gwarantuje to, że Observer otrzymają powiadomienie, gdy komponent jest już w pełni gotowy do nowego stanu. Używanie LifecycleRegistry.createUnsafe jako alternatywy również jest możliwe, ale wymaga ostrożności z wątkami.
LifecycleOwner jest fundamentem dla kilku kluczowych komponentów Android Jetpack. LiveData używa LifecycleOwner do określania aktywnego stanu i automatycznego wypisywania przy zniszczeniu komponentu. ViewModel nie implementuje LifecycleOwner bezpośrednio, ale może otrzymywać Lifecycle przez SavedStateHandle. Navigation Component używa LifecycleOwner do zarządzania subskrypcjami w NavBackStackEntry. Zrozumienie tych powiązań pomaga budować architekturę aplikacji na solidnym fundamencie.
Interakcja LiveData z LifecycleOwner:
class ExampleActivity : AppCompatActivity() {
private val viewModel: ExampleViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewModel.userData.observe(this) { data ->
// this — LifecycleOwner (Activity)
// Kod jest wykonywany tylko, gdy Activity jest w stanie RESUMED
updateUI(data)
}
}
}
LiveData wymaga LifecycleOwner w metodzie observe(), ponieważ gwarantuje to, że aktualizacje UI będą występować tylko w aktywnym stanie. Jeśli Activity jest w tle, LiveData zachowuje ostatnią wartość, ale nie powiadamia Observer. Po powrocie do RESUMED Observer otrzymuje aktualną wartość bez dodatkowych zapytań do sieci lub bazy danych.
DataBinding również używa LifecycleOwner do powiązania pól observable z cyklem życia Activity lub Fragment. Pozwala to uniknąć wycieków pamięci w połączeniu ViewModel + DataBinding — wszystkie subskrypcje są automatycznie czyszczone przy zniszczeniu LifecycleOwner. Takie podejście czyni kod deklaratywnym i bezpiecznym.
Prawidłowe używanie LifecycleOwner wymaga przestrzegania kilku kluczowych zasad. Pierwsza i najważniejsza: zawsze subskrybuj Observer w onCreate/onViewCreated, a nie później. Gwarantuje to, że Observer otrzyma początkowy stan Lifecycle (CREATED po onCreate) i nie przegapi zdarzeń. Druga zasada: używaj DefaultLifecycleObserver zamiast podejścia adnotacyjnego dla wszystkich nowych projektów.
Nowoczesne podejście do pracy z korutynami i LifecycleOwner — rozszerzenie repeatOnLifecycle:
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.flow.collect { value ->
updateUI(value)
}
}
}
Ten wzorzec gwarantuje, że collect na Flow jest aktywna tylko w stanie STARTED lub RESUMED. Przy przejściu w STOPPED kolekcja jest automatycznie anulowana, a po powrocie do STARTED — restartowana. repeatOnLifecycle zastępuje ręczne wypisywanie z Flow we Fragment i jest zalecanym przez Google podejściem do pracy z asynchronicznymi strumieniami danych w komponentach UI.
Kolejne ważne zalecenie: nie nadużywaj LifecycleObserver do logiki niezwiązanej z cyklem życia. Jeśli komponent ma wykonać działanie przy określonym stanie, ale nie wymaga wypisywania przy zniszczeniu, lepiej użyć jawnego wywołania metod w onStart/onStop. LifecycleObserver jest uzasadniony dla długożyjących komponentów (LocationListener, SensorManager), gdzie ręczne zarządzanie subskrypcjami jest skomplikowane i podatne na błędy.
Często zadawane pytania
LifecycleOwner — to interfejs, który deklaruje, że obiekt ma cykl życia. Lifecycle — to klasa przechowująca bieżący stan i zarządzająca Observer. LifecycleOwner udostępnia Lifecycle przez getLifecycle().
Nie, Lifecycle automatycznie wypisuje wszystkich Observer przy przejściu w DESTROYED. To jedna z głównych zalet LifecycleOwner — programista nie musi ręcznie wywoływać removeObserver w onDestroy.
Fragment implementuje LifecycleOwner przez interfejs fragmentów AndroidX. Jego Lifecycle jest powiązany z cyklem życia Fragment oddzielnie od Activity. Pozwala to Observer reagować właśnie na zdarzenia Fragment, a nie nadrzędnego Activity.
Tak, do tego służy LifecycleRegistry. Custom View musi zaimplementować interfejs LifecycleOwner i ręcznie aktualizować stan LifecycleRegistry przy zmianie widoczności lub dołączeniu do okna.
LifecycleOwner rozwiązuje inne zadanie: zarządzanie subskrypcjami zdarzeń cyklu życia, a nie anulowanie korutyn. Do korutyn używa się lifecycleScope, który automatycznie anuluje uruchomione korutyny przy zniszczeniu LifecycleOwner.
Podsumowanie
Opracujemy aplikację mobilną pod klucz
IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.
Przeczytaj również