LiveData: co to jest, komponent Android Architecture

Autor: IT Sectr Opublikowano: 2026-02-20 Czas czytania: 8 min

LiveData — obserwowalny kontener danych z Android Jetpack, uwzględniający cykl życia Activity, Fragment lub Service. Omówimy, jak LiveData automatycznie zarządza subskrypcjami: aktywni subskrybenci otrzymują aktualizacje, a nieaktywni — nie, co eliminuje wycieki pamięci i crash'e spowodowane nieaktualnymi referencjami. Według danych Google (Android Developers, 2025), LiveData jest używane w 74% projektów w Java i Kotlin jako główny sposób reaktywnego przesyłania danych z ViewModel do UI.

Najważniejsze

  • LiveData — obserwowalny kontener danych z uwzględnieniem cyklu życia: automatycznie wypisuje się przy nieaktywności subskrybenta.
  • MutableLiveData — modyfikowalna wersja LiveData z metodami setValue() (główny wątek) i postValue() (tło).
  • Observer — interfejs otrzymujący aktualizacje przy zmianie danych, gdy LifecycleOwner jest w stanie aktywnym.
  • Transformacje map() i switchMap() — funkcjonalne łańcuchy przekształcania LiveData bez tworzenia nowych klas.
  • MediatorLiveData — łączenie kilku źródeł LiveData w jeden strumień z priorytetowym zarządzaniem.

Czym jest LiveData w Android?

LiveData — to klasa z biblioteki Android Jetpack implementująca wzorzec Observer z uwzględnieniem cyklu życia. W przeciwieństwie do standardowych Observable czy Flow, LiveData automatycznie zarządza subskrypcjami: Observer otrzymuje powiadomienia tylko gdy LifecycleOwner znajduje się w stanie aktywnym (STARTED lub RESUMED). Jeśli właściciel cyklu życia przechodzi w stan nieaktywny (STOPPED lub DESTROYED), subskrypcja jest wstrzymywana lub usuwana.

LiveData został przedstawiony w Android Architecture Components (AAC) w 2017 roku na Google I/O wraz z ViewModel i Room. Główną motywacją było wyeliminowanie problemu wycieków pamięci podczas pracy z danymi asynchronicznymi: programiści często zapominali wypisywać się z callbacków, co prowadziło do utrzymywania referencji do zniszczonych Activity. LiveData automatyzuje wypisywanie — Observer powiązany z LifecycleOwner nie otrzyma aktualizacji po zniszczeniu właściciela.

Według ankiety Android Developers (2025), co drugi crash przed wdrożeniem LiveData był związany z wywoływaniem metod na zniszczonym kontrolerze UI. LiveData całkowicie eliminuje tę klasę błędów. W IT Sectr wdrożyliśmy LiveData we wszystkich projektach od 2018 roku — przez 7 lat ani jednego crash'a z powodu nieaktualnej referencji do Activity.

LiveData i Lifecycle: jak działa automatyczna subskrypcja

Kluczowa różnica LiveData od innych kontenerów obserwowalnych — powiązanie z Lifecycle. Podczas tworzenia obserwatora LiveData sprawdza status LifecycleOwner: jeśli status to STARTED lub RESUMED, Observer jest uznawany za aktywnego i otrzymuje aktualizacje natychmiast. Jeśli status to PAUSED, STOPPED lub DESTROYED, aktualizacje nie są dostarczane do momentu powrotu do stanu aktywnego.

Mechanizm jest zaimplementowany przez klasę LifecycleBoundObserver, która rejestruje się w Lifecycle za pomocą addObserver(). Gdy LifecycleOwner zmienia stan, wywoływany jest callback onStateChanged(), a LiveData aktualizuje status aktywności Observer. Podczas ustawiania danych przez setValue() LiveData przechodzi przez listę obserwatorów i dostarcza wartość tylko aktywnym. Gdy obserwator przechodzi w stan DESTROYED, Observer jest automatycznie usuwany z listy subskrybentów.

Według dokumentacji Android Jetpack (2025), mechanizm LifecycleBoundObserver zużywa mniej niż 0,5 µs na sprawdzenie statusu — narzut jest pomijalnie mały w porównaniu z typową operacją aktualizacji UI. To sprawia, że LiveData nadaje się do wysokoczęstotliwościowych aktualizacji (timery, liczniki) bez ryzyka spadku wydajności.

MutableLiveData: setValue vs postValue

MutableLiveData — dziedziczy po LiveData z otwartymi metodami setValue() i postValue() do zmiany przechowywanej wartości. W przeciwieństwie do LiveData, MutableLiveData jest dostępny do zapisu, ale w ViewModel przyjęło się publikować tylko LiveData (niezmienną wersję), ukrywając MutableLiveData pod modyfikatorem private.

kotlin
class SearchViewModel : ViewModel() {
    private val _query = MutableLiveData("")
    val query: LiveData<String> get() = _query

    fun updateQuery(newQuery: String) {
        _query.value = newQuery  // setValue() — na głównym wątku
    }

    fun updateFromNetwork(result: String) {
        _query.postValue(result)  // postValue() — z dowolnego wątku
    }
}

setValue() musi być wywoływany tylko z głównego wątku (main thread) — natychmiast powiadamia obserwatorów. postValue() jest bezpieczny do wywołania z wątku tła: umieszcza wartość w kolejce głównego wątku i powiadamia obserwatorów asynchronicznie. Ważne: jeśli postValue() jest wywołany dwa razy z rzędu przed przetworzeniem pierwszego, wartość pośrednia może zostać utracona — obserwatorzy otrzymają tylko ostatnią. Do przekazywania wszystkich stanów pośrednich (np. postęp ładowania) używaj setValue() na głównym wątku.

Transformacje LiveData: map, switchMap, MediatorLiveData

Transformations.map() — funkcjonalne przekształcenie wartości jednego LiveData na inny typ bez pisania Observer. Na przykład z LiveData<User> uzyskać LiveData<String> z nazwą użytkownika. Transformacje są leniwe: przekształcenie wykonuje się tylko przy aktywnym Observerze na docelowym LiveData.

kotlin
val userLiveData: LiveData<User> = ...
val userName: LiveData<String> = Transformations.map(userLiveData) { user ->
    "${user.firstName} ${user.lastName}"
}

val userIdLiveData: LiveData<String> = ...
val userDetails: LiveData<UserDetails> = Transformations.switchMap(userIdLiveData) { id ->
    repository.getUserDetails(id)
}

// MediatorLiveData — łączenie dwóch źródeł
val mediator = MediatorLiveData<CombinedState>()
mediator.addSource(priceLiveData) { price ->
    mediator.value = CombinedState(price, countLiveData.value)
}
mediator.addSource(countLiveData) { count ->
    mediator.value = CombinedState(priceLiveData.value, count)
}

Transformations.switchMap() — odpowiednik flatMap ze świata strumieni reaktywnych: przy zmianie wejściowego LiveData przełącza się na nową instancję wyjściowego LiveData. MediatorLiveData — zaawansowane narzędzie do łączenia wielu źródeł LiveData z możliwością zarządzania priorytetem aktualizacji. Według Developer Survey (2024), MediatorLiveData jest używane w 35% projektów wymagających agregacji danych z różnych źródeł — na przykład łączenie danych z formularza UI i odpowiedzi serwera.

LiveData z korutynami: liveData builder

liveData { } — coroutine builder (wprowadzony w lifecycle-livedata-ktx 2.2.0), umożliwiający asynchroniczne obliczanie wartości LiveData wewnątrz korutyny. Wewnątrz bloku liveData { } dostępny jest kontekst suspend, a także funkcja emit() do publikowania wartości. Wszystkie korutyny uruchomione wewnątrz buildera są automatycznie anulowane przy nieaktywności wszystkich obserwatorów.

kotlin
val userLiveData: LiveData<User> = liveData {
    // Wykonywane na Dispatchers.IO domyślnie
    val user = userRepository.fetchUser(userId)
    // Emitujemy wynik — automatycznie na głównym wątku
    emit(user)
}

val progressLiveData: LiveData<Int> = liveData {
    for (i in 0..100) {
        emit(i)
        delay(50)
    }
}

liveData builder wspiera emitSource() — emisję innego LiveData jako źródła (odpowiednik switchMap wewnątrz korutyny). Timeout: jeśli żaden Observer nie jest aktywny przez 5 sekund (domyślnie), korutyna jest anulowana. Po ponownej aktywacji liveData { } wykonuje się od nowa. Według Google (Android Dev Summit 2024), liveData builder redukuje 40% kodu boilerplate w porównaniu z ręcznym zarządzaniem ViewModel + LiveData.

Przykłady kodu: LiveData w Kotlin

Przykład 1: ViewModel z LiveData dla ekranu logowania

Klasyczny ekran logowania z polami email i hasło, walidacją i stanem ładowania. ViewModel zarządza trzema LiveData: email, password i loginResult.

kotlin
class LoginViewModel : ViewModel() {
    private val _email = MutableLiveData("")
    val email: LiveData<String> get() = _email

    private val _password = MutableLiveData("")
    val password: LiveData<String> get() = _password

    private val _loginResult = MutableLiveData<Result<User>>()
    val loginResult: LiveData<Result<User>> get() = _loginResult

    fun onEmailChanged(text: String) {
        _email.value = text
    }

    fun onPasswordChanged(text: String) {
        _password.value = text
    }

    fun login() {
        if (_email.value.isNullOrBlank() || _password.value.isNullOrBlank()) {
            _loginResult.value = Result.failure(IllegalArgumentException("Wypełnij wszystkie pola"))
            return
        }
        viewModelScope.launch {
            try {
                val user = authRepository.login(_email.value!!, _password.value!!)
                _loginResult.value = Result.success(user)
            } catch (e: Exception) {
                _loginResult.value = Result.failure(e)
            }
        }
    }
}

Przykład 2: LiveData z Room i korutynami

Room wspiera LiveData jako typ zwracany zapytania DAO: przy każdej zmianie tabeli LiveData automatycznie powiadamia obserwatorów, co jest idealne dla reaktywnego UI.

kotlin
@Dao
interface TaskDao {
    @Query("SELECT * FROM tasks WHERE completed = 0")
    fun getActiveTasks(): LiveData<List<Task>>

    @Insert
    suspend fun insertTask(task: Task)
}

// W ViewModel:
class TaskViewModel(application: Application) : AndroidViewModel(application) {
    private val dao = AppDatabase.getDatabase(application).taskDao()
    val activeTasks: LiveData<List<Task>> = dao.getActiveTasks()
}

Room generuje kod śledzący zmiany w tabeli tasks i automatycznie aktualizuje LiveData przy każdym INSERT, UPDATE lub DELETE. Działa to bez dodatkowego kodu — wystarczy adnotacja @Query z typem zwracanym LiveData. W IT Sectr używamy Room + LiveData jako standardowego stosu do lokalnego buforowania danych w projektach Android od 2019 roku.

Często zadawane pytania

Jaka jest różnica między LiveData a StateFlow?

LiveData — obserwowalny kontener z wbudowanym wsparciem Lifecycle: Observer automatycznie aktywuje się/deaktywuje. StateFlow — strumień reaktywny z Kotlin Coroutines (Kotlinx Coroutines 1.3.7+), nieprzywiązany do Lifecycle, ale obsługujący to przez stateIn(WhileSubscribed). StateFlow wymaga jawnego zarządzania cyklem życia w View, ale daje dostęp do korutyn, operatorów Flow i wieloplatformowości. Google zaleca StateFlow dla nowych projektów w Kotlin, LiveData — dla kodu w Javie lub przy potrzebie zgodności ze starymi bibliotekami.

Jak przekonwertować LiveData na StateFlow?

Użyj funkcji rozszerzającej liveData.asFlow() z biblioteki lifecycle-livedata-ktx. Tworzy ona Flow emitujący bieżącą wartość LiveData przy każdej zmianie. Następnie przekonwertuj na StateFlow przez .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), initialValue). Odwrotna konwersja — stateFlow.asLiveData(). Wzajemna konwersja pozwala wykorzystać zalety obu bibliotek w jednym projekcie.

Kiedy LiveData traci dane przy postValue?

postValue() używa AtomicReference do przechowywania opóźnionej wartości. Jeśli postValue() jest wywołany dwa razy przed przetworzeniem przez główny wątek, pierwsza wartość zostanie nadpisana drugą — Observable otrzyma tylko ostatnią. Wynika to z faktu, że LiveData nie ma wewnętrznej kolejki: przechowuje tylko jedną opóźnioną wartość. Do przekazywania każdego punktu pośredniego (1%, 2%, … 100%) używaj setValue() na głównym wątku lub ConflatedFlow z kotlinx-coroutines.

Czy można używać LiveData bez LifecycleOwner?

Tak, LiveData można obserwować przez observeForever(), przekazując Observer bez LifecycleOwner. Jednak w tym przypadku wypisanie musi być jawne przez removeObserver() — automatyczne wypisanie nie działa. observeForever() jest używane w serwisach, ContentProvider lub ViewModel, gdzie LifecycleOwner jest niedostępny. Zgodnie z zaleceniami Google, unikaj observeForever() w Activity/Fragment — używaj observe() z LifecycleOwner.

Czym jest LiveData wersji 1.0 (zawsze aktualne)?

Cecha zachowania: gdy LiveData otrzymuje nowego aktywnego Observera, natychmiast otrzymuje on ostatnią wartość (jeśli jest ustawiona). Stare wersje LiveData (przed lifecycle 2.5.0) dostarczały wartość nawet nieaktywnym subskrybentom przy przejściu w stan aktywny — zostało to naprawione. W aktualnej wersji LiveData otrzymuje ostatnią wartość przy przejściu Z nieaktywnego DO aktywnego stanu, co upraszcza inicjalizację ekranów.

Podsumowanie

  • LiveData — obserwowalny kontener danych z automatycznym powiązaniem z Lifecycle, eliminujący wycieki pamięci i crash'e z powodu nieaktualnych referencji.
  • MutableLiveData z setValue() (główny wątek) i postValue() (tło) — główne API do zmiany danych.
  • Transformacje map(), switchMap() i MediatorLiveData — funkcjonalne łańcuchy bez kodu boilerplate.
  • liveData builder liveData { } — podejście korutynowe do tworzenia asynchronicznych LiveData z automatycznym anulowaniem korutyn.
  • Room + LiveData — gotowe połączenie do lokalnego buforowania bez dodatkowego kodu w DAO.
  • LiveData jest używane w 74% projektów Jetpack i pozostaje standardem dla kodu w Javie i architektur legacy.
  • Dla nowych projektów w Kotlin Google zaleca StateFlow, ale LiveData pozostaje zgodnym rozwiązaniem dla hybrydowych stosów.

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.

Omów projekt

Przeczytaj również