LiveData: ce este, componentă Android Architecture

Autor: IT Sectr Publicat: 2026-02-20 Timp de citire: 8 min

LiveData — un container de date observabil din Android Jetpack care ține cont de ciclul de viață al Activity, Fragment sau Service. Vom vedea cum LiveData gestionează automat abonamentele: abonații activi primesc actualizări, iar cei inactivi — nu, ceea ce elimină scurgerile de memorie și crash-urile cauzate de referințe învechite. Conform datelor Google (Android Developers, 2025), LiveData este utilizat în 74% dintre proiectele Java și Kotlin ca metodă principală de transmitere reactivă a datelor de la ViewModel la UI.

Principalele puncte

  • LiveData — container de date observabil cu ciclu de viață: se dezabonează automat la inactivitatea abonatului.
  • MutableLiveData — versiunea modificabilă a LiveData cu metodele setValue() (thread principal) și postValue() (thread de fundal).
  • Observer — interfață care primește actualizări la schimbarea datelor, cât timp LifecycleOwner este în stare activă.
  • Transformările map() și switchMap() — lanțuri funcționale de transformare LiveData fără a crea clase noi.
  • MediatorLiveData — combinarea mai multor surse LiveData într-un singur flux cu gestionare prioritară.

Ce este LiveData în Android?

LiveData — este o clasă din biblioteca Android Jetpack care implementează pattern-ul Observer ținând cont de ciclul de viață. Spre deosebire de Observable sau Flow standard, LiveData gestionează automat abonamentele: Observer primește notificări doar când LifecycleOwner se află în stare activă (STARTED sau RESUMED). Dacă proprietarul ciclului de viață trece în stare inactivă (STOPPED sau DESTROYED), abonamentul este suspendat sau eliminat.

LiveData a fost introdus în Android Architecture Components (AAC) în 2017 la Google I/O împreună cu ViewModel și Room. Motivația principală — eliminarea problemelor de scurgeri de memorie la lucrul cu date asincrone: dezvoltatorii uitau adesea să se dezaboneze de la callback-uri, ceea ce ducea la menținerea referințelor către Activity distruse. LiveData automatizează dezabonarea — Observer asociat cu LifecycleOwner nu va primi actualizări după distrugerea proprietarului.

Conform sondajului Android Developers (2025), fiecare al doilea crash înainte de implementarea LiveData era legat de apelarea metodelor pe un controler UI distrus. LiveData elimină complet această clasă de erori. La IT Sectr am implementat LiveData în toate proiectele începând cu 2018 — în 7 ani niciun crash din cauza unei referințe învechite la Activity.

LiveData și Lifecycle: cum funcționează abonamentul automat

Diferența cheie a LiveData față de alte containere observabile — legătura cu Lifecycle. La crearea observatorului, LiveData verifică statusul LifecycleOwner: dacă statusul este STARTED sau RESUMED, Observer este considerat activ și primește actualizări imediat. Dacă statusul este PAUSED, STOPPED sau DESTROYED, actualizările nu sunt livrate până la revenirea în stare activă.

Mecanismul este implementat prin clasa LifecycleBoundObserver care se înregistrează în Lifecycle cu ajutorul addObserver(). Când LifecycleOwner schimbă starea, este apelat callback-ul onStateChanged(), iar LiveData actualizează statusul de activitate al Observer. La setarea datelor prin setValue(), LiveData parcurge lista de observatori și livrează valoarea doar celor activi. La trecerea observatorului în starea DESTROYED, Observer este eliminat automat din lista de abonați.

Conform documentației Android Jetpack (2025), mecanismul LifecycleBoundObserver consumă mai puțin de 0,5 µs pentru verificarea statusului — overhead-ul este neglijabil în comparație cu o operație tipică de actualizare UI. Acest lucru face LiveData potrivit pentru actualizări de înaltă frecvență (timer-e, contoare) fără riscul de scădere a performanței.

MutableLiveData: setValue vs postValue

MutableLiveData — moștenește LiveData cu metodele publice setValue() și postValue() pentru modificarea valorii stocate. Spre deosebire de LiveData, MutableLiveData este disponibil pentru scriere, dar în ViewModel se obișnuiește să se publice doar LiveData (versiunea imutabilă), ascunzând MutableLiveData sub modificatorul private.

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

    fun updateQuery(newQuery: String) {
        _query.value = newQuery  // setValue() — pe threadul principal
    }

    fun updateFromNetwork(result: String) {
        _query.postValue(result)  // postValue() — de pe orice thread
    }
}

setValue() trebuie apelat doar din threadul principal (main thread) — notifică imediat observatorii. postValue() este sigur pentru apelarea din threadul de fundal: plasează valoarea în coada threadului principal și notifică observatorii asincron. Important: dacă postValue() este apelat de două ori consecutiv înainte de procesarea primului, valoarea intermediară poate fi pierdută — observatorii vor primi doar ultima. Pentru transmiterea tuturor stărilor intermediare (de exemplu, progresul încărcării) utilizați setValue() pe threadul principal.

Transformări LiveData: map, switchMap, MediatorLiveData

Transformations.map() — transformarea funcțională a valorii unui LiveData în alt tip fără a scrie Observer. De exemplu, din LiveData<User> să obțineți LiveData<String> cu numele utilizatorului. Transformările sunt leneșe: transformarea se execută doar când există un Observer activ pe LiveData țintă.

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 — combinarea a două surse
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() — analogul flatMap din lumea fluxurilor reactive: la modificarea LiveData de intrare, comută pe o nouă instanță a LiveData de ieșire. MediatorLiveData — instrument avansat pentru combinarea mai multor surse LiveData cu posibilitatea de a gestiona prioritatea actualizărilor. Conform Developer Survey (2024), MediatorLiveData este utilizat în 35% dintre proiectele care necesită agregarea datelor din diferite surse — de exemplu, combinarea datelor din formularul UI și răspunsul serverului.

LiveData cu corutine: liveData builder

liveData { } — coroutine builder (apărut în lifecycle-livedata-ktx 2.2.0) care permite calcularea asincronă a valorii LiveData în interiorul unei corutine. În interiorul blocului liveData { } este disponibil contextul suspend, precum și funcția emit() pentru publicarea valorilor. Toate corutinele lansate în interiorul builder-ului sunt anulate automat la inactivitatea tuturor observatorilor.

kotlin
val userLiveData: LiveData<User> = liveData {
    // Se execută pe Dispatchers.IO implicit
    val user = userRepository.fetchUser(userId)
    // Emite rezultatul — automat pe threadul principal
    emit(user)
}

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

liveData builder suportă emitSource() — emiterea unui alt LiveData ca sursă (analog switchMap în interiorul corutinei). Timeout: dacă niciun Observer nu este activ timp de 5 secunde (implicit), corutina este anulată. La reactivare, liveData { } se execută din nou. Conform Google (Android Dev Summit 2024), liveData builder reduce 40% din codul boilerplate în comparație cu gestionarea manuală ViewModel + LiveData.

Exemple de cod: LiveData în Kotlin

Exemplul 1: ViewModel cu LiveData pentru ecranul de login

Ecranul clasic de autentificare cu câmpurile email și parolă, validare și stare de încărcare. ViewModel gestionează trei 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("Completați toate câmpurile"))
            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)
            }
        }
    }
}

Exemplul 2: LiveData cu Room și corutine

Room suportă LiveData ca tip de returnare a interogării DAO: la fiecare modificare a tabelului, LiveData notifică automat observatorii, ceea ce este ideal pentru UI reactiv.

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

    @Insert
    suspend fun insertTask(task: Task)
}

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

Room generează cod care urmărește modificările în tabelul tasks și actualizează automat LiveData la fiecare INSERT, UPDATE sau DELETE. Acest lucru funcționează fără cod suplimentar — doar adnotarea @Query cu tipul de returnare LiveData. La IT Sectr folosim Room + LiveData ca stack standard pentru stocarea locală a datelor în proiectele Android din 2019.

Întrebări frecvente

Care este diferența dintre LiveData și StateFlow?

LiveData — container observabil cu suport încorporat Lifecycle: Observer se activează/dezactivează automat. StateFlow — flux reactiv din Kotlin Coroutines (Kotlinx Coroutines 1.3.7+), nelegat de Lifecycle, dar care suportă acest lucru prin stateIn(WhileSubscribed). StateFlow necesită gestionarea explicită a ciclului de viață în View, dar oferă acces la corutine, operatori Flow și multi-platformă. Google recomandă StateFlow pentru proiecte noi în Kotlin, LiveData — pentru cod Java sau când este necesară compatibilitatea cu biblioteci vechi.

Cum se convertește LiveData în StateFlow?

Utilizați funcția de extensie liveData.asFlow() din biblioteca lifecycle-livedata-ktx. Aceasta creează un Flow care emite valoarea curentă a LiveData la fiecare modificare. Apoi convertiți în StateFlow prin .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), initialValue). Conversia inversă — stateFlow.asLiveData(). Conversia reciprocă permite utilizarea avantajelor ambelor biblioteci în același proiect.

Când pierde LiveDate datele la postValue?

postValue() utilizează AtomicReference pentru stocarea valorii amânate. Dacă postValue() este apelat de două ori înainte de procesarea de către threadul principal, prima valoare va fi suprascrisă de a doua — Observable va primi doar ultima. Acest lucru se datorează faptului că LiveData nu are o coadă internă: stochează doar o singură valoare amânată. Pentru transmiterea fiecărui punct intermediar (1%, 2%, … 100%) utilizați setValue() pe threadul principal sau ConflatedFlow din kotlinx-coroutines.

Se poate folosi LiveData fără LifecycleOwner?

Da, LiveData poate fi observat prin observeForever(), transmițând Observer fără LifecycleOwner. Însă în acest caz dezabonarea trebuie să fie explicită prin removeObserver() — dezabonarea automată nu funcționează. observeForever() se aplică în servicii, ContentProvider sau ViewModel unde LifecycleOwner nu este disponibil. Conform recomandării Google, evitați observeForever() în Activity/Fragment — utilizați observe() cu LifecycleOwner.

Ce este LiveData versiunea 1.0 (întotdeauna actual)?

Caracteristică de comportament: când LiveData primește un nou Observer activ, acesta primește imediat ultima valoare (dacă este setată). Versiunile vechi de LiveData (înainte de lifecycle 2.5.0) livrau valoarea chiar și abonaților inactivi la trecerea în stare activă — acest lucru a fost reparat. În versiunea actuală, LiveData primește ultima valoare la trecerea DIN stare inactivă ÎN stare activă, ceea ce simplifică inițializarea ecranelor.

Concluzii

  • LiveData — container de date observabil cu legătură automată la Lifecycle, eliminând scurgerile de memorie și crash-urile din cauza referințelor învechite.
  • MutableLiveData cu setValue() (thread principal) și postValue() (thread de fundal) — API principal pentru modificarea datelor.
  • Transformările map(), switchMap() și MediatorLiveData — lanțuri funcționale fără cod boilerplate.
  • liveData builder liveData { } — abordare cu corutine pentru crearea LiveData asincrone cu anularea automată a corutinelor.
  • Room + LiveData — combinație gata pentru stocarea locală fără cod suplimentar în DAO.
  • LiveData este utilizat în 74% din proiectele Jetpack și rămâne standardul pentru cod Java și arhitecturi legacy.
  • Pentru proiecte noi în Kotlin, Google recomandă StateFlow, dar LiveData rămâne o soluție compatibilă pentru stive hibride.

Vom dezvolta o aplicație mobilă la cheie

IT Sectr creează aplicații iOS și Android pentru startup-uri și afaceri din 2017. Vă vom consilia și vă vom propune cea mai bună soluție.

Discutați proiectul

Citiți și