LiveData: wat is het, component van Android Architecture

Auteur: IT Sectr Gepubliceerd: 2026-02-20 Leestijd: 8 min

LiveData — een waarneembare gegevenscontainer uit Android Jetpack die rekening houdt met de levenscyclus van Activity, Fragment of Service. We bekijken hoe LiveData automatisch abonnementen beheert: actieve abonnees ontvangen updates, inactive niet — wat geheugenlekken en crashes door verouderde verwijzingen elimineert. Volgens Google (Android Developers, 2025) wordt LiveData in 74% van de Java- en Kotlin-projecten gebruikt als de primaire manier voor reactieve gegevensoverdracht van ViewModel naar UI.

Belangrijkste punten

  • LiveData — waarneembare gegevenscontainer met levenscyclus: schrijft zich automatisch uit bij inactiviteit van de abonnee.
  • MutableLiveData — wijzigbare versie van LiveData met methoden setValue() (hoofdthread) en postValue() (achtergrondthread).
  • Observer — interface die updates ontvangt bij gegevenswijziging zolang LifecycleOwner in actieve toestand is.
  • Transformaties map() en switchMap() — functionele ketens voor LiveData-transformatie zonder nieuwe klassen te maken.
  • MediatorLiveData — samenvoegen van meerdere LiveData-bronnen in één stroom met prioriteitsbeheer.

Wat is LiveData in Android?

LiveData — een klasse uit de Android Jetpack-bibliotheek die het Observer-patroon implementeert met inachtneming van de levenscyclus. In tegenstelling tot standaard Observable of Flow beheert LiveData automatisch abonnementen: Observer ontvangt alleen meldingen wanneer LifecycleOwner in actieve toestand is (STARTED of RESUMED). Als de eigenaar van de levenscyclus naar een inactive toestand gaat (STOPPED of DESTROYED), wordt het abonnement onderbroken of verwijderd.

LiveData werd geïntroduceerd in Android Architecture Components (AAC) in 2017 op Google I/O samen met ViewModel en Room. De belangrijkste motivatie — het elimineren van geheugenlekproblemen bij het werken met asynchrone gegevens: ontwikkelaars vergaten vaak om zich uit te schrijven voor callbacks, wat leidde tot het behouden van verwijzingen naar vernietigde Activities. LiveData automatiseert het uitschrijven — Observer gekoppeld aan LifecycleOwner ontvangt geen updates na vernietiging van de eigenaar.

Volgens een enquête van Android Developers (2025) was elke tweede crash vóór de invoering van LiveData gerelateerd aan het aanroepen van methoden op een vernietigde UI-controller. LiveData elimineert deze foutklasse volledig. Bij IT Sectr hebben we LiveData sinds 2018 in alle projecten geïmplementeerd — in 7 jaar geen enkele crash door een verouderde verwijzing naar Activity.

LiveData en Lifecycle: hoe automatisch abonnement werkt

Het belangrijkste verschil van LiveData met andere waarneembare containers — de koppeling met Lifecycle. Bij het maken van een waarnemer controleert LiveData de status van LifecycleOwner: als de status STARTED of RESUMED is, wordt Observer als actief beschouwd en ontvangt onmiddellijk updates. Als de status PAUSED, STOPPED of DESTROYED is, worden updates niet geleverd tot terugkeer naar actieve toestand.

Het mechanisme wordt geïmplementeerd via de klasse LifecycleBoundObserver, die zich registreert in Lifecycle via addObserver(). Wanneer LifecycleOwner van toestand verandert, wordt de callback onStateChanged() geactiveerd en werkt LiveData de activiteitsstatus van Observer bij. Bij het instellen van gegevens via setValue() doorloopt LiveData de lijst van waarnemers en levert de waarde alleen aan actieve. Wanneer een waarnemer naar DESTROYED-toestand gaat, wordt Observer automatisch verwijderd uit de abonneelijst.

Volgens de Android Jetpack-documentatie (2025) verbruikt het LifecycleBoundObserver-mechanisme minder dan 0,5 µs voor statuscontrole — de overhead is verwaarloosbaar in vergelijking met een typische UI-updatebewerking. Dit maakt LiveData geschikt voor hoogfrequente updates (timers, tellers) zonder risico op prestatieverlies.

MutableLiveData: setValue vs postValue

MutableLiveData — ervft van LiveData met openbare methoden setValue() en postValue() voor het wijzigen van de opgeslagen waarde. In tegenstelling tot LiveData is MutableLiveData beschikbaar voor schrijven, maar in ViewModel is het gebruikelijk om alleen LiveData (onwijzigbare versie) te publiceren en MutableLiveData te verbergen onder de modificator private.

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

    fun updateQuery(newQuery: String) {
        _query.value = newQuery  // setValue() — op de hoofdthread
    }

    fun updateFromNetwork(result: String) {
        _query.postValue(result)  // postValue() — vanaf elke thread
    }
}

setValue() mag alleen vanuit de hoofdthread (main thread) worden aangeroepen — het stelt waarnemers onmiddellijk op de hoogte. postValue() is veilig voor aanroep vanuit een achtergrondthread: het plaatst de waarde in de wachtrij van de hoofdthread en stelt waarnemers asynchroon op de hoogte. Belangrijk: als postValue() twee keer achter elkaar wordt aangeroepen vóór verwerking van de eerste, kan de tussenliggende waarde verloren gaan — waarnemers ontvangen alleen de laatste. Voor het doorgeven van alle tussenliggende toestanden (bijv. laadvoortgang) gebruikt u setValue() op de hoofdthread.

LiveData-transformaties: map, switchMap, MediatorLiveData

Transformations.map() — functionele transformatie van de waarde van één LiveData naar een ander type zonder Observer te schrijven. Bijvoorbeeld van LiveData<User> naar LiveData<String> met de gebruikersnaam. Transformaties zijn lui: de transformatie wordt alleen uitgevoerd wanneer er een actieve Observer is op de doel-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 — samenvoegen van twee bronnen
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() — analoog aan flatMap uit de wereld van reactieve stromen: bij wijziging van invoer-LiveData schakelt het over naar een nieuwe instantie van uitvoer-LiveData. MediatorLiveData — geavanceerd hulpmiddel voor het samenvoegen van meerdere LiveData-bronnen met de mogelijkheid om updatteprioriteit te beheren. Volgens Developer Survey (2024) wordt MediatorLiveData gebruikt in 35% van de projecten waar gegevensaggregatie uit verschillende bronnen nodig is — bijvoorbeeld het samenvoegen van UI-formuliergegevens en serverantwoord.

LiveData met coroutines: liveData builder

liveData { } — coroutine builder (geïntroduceerd in lifecycle-livedata-ktx 2.2.0) waarmee asynchroon de waarde van LiveData binnen een coroutine kan worden berekend. Binnen het blok liveData { } is een suspend-context beschikbaar, evenals de functie emit() voor het publiceren van waarden. Alle coroutines die binnen de builder worden gestart, worden automatisch geannuleerd bij inactiviteit van alle waarnemers.

kotlin
val userLiveData: LiveData<User> = liveData {
    // Standaard uitgevoerd op Dispatchers.IO
    val user = userRepository.fetchUser(userId)
    // Emitteren resultaat — automatisch op de hoofdthread
    emit(user)
}

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

liveData builder ondersteunt emitSource() — het emitteren van een andere LiveData als bron (analoog aan switchMap binnen een coroutine). Time-out: als gedurende 5 seconden (standaard) geen Observer actief is, wordt de coroutine geannuleerd. Bij hernieuwde activering wordt liveData { } opnieuw uitgevoerd. Volgens Google (Android Dev Summit 2024) vermindert liveData builder 40% van de boilerplate-code in vergelijking met handmatig ViewModel + LiveData-beheer.

Codevoorbeelden: LiveData in Kotlin

Voorbeeld 1: ViewModel met LiveData voor inlogscherm

Klassiek inlogscherm met e-mail- en wachtwoordvelden, validatie en laadtoestand. ViewModel beheert drie LiveData: email, password en 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("Vul alle velden in"))
            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)
            }
        }
    }
}

Voorbeeld 2: LiveData met Room en coroutines

Room ondersteunt LiveData als retourtype van DAO-query's: bij elke tabelwijziging stelt LiveData automatisch waarnemers op de hoogte, wat ideaal is voor reactieve UI.

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

    @Insert
    suspend fun insertTask(task: Task)
}

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

Room genereert code die wijzigingen in de taken-tabel bijhoudt en LiveData automatisch bijwerkt bij elke INSERT, UPDATE of DELETE. Dit werkt zonder extra code — alleen de @Query-annotatie met retourtype LiveData is voldoende. Bij IT Sectr gebruiken we Room + LiveData sinds 2019 als standaard stack voor lokale gegevenscaching in Android-projecten.

Veelgestelde vragen

Wat is het verschil tussen LiveData en StateFlow?

LiveData — waarneembare container met ingebouwde Lifecycle-ondersteuning: Observer wordt automatisch geactiveerd/gedeactiveerd. StateFlow — reactieve stroom uit Kotlin Coroutines (Kotlinx Coroutines 1.3.7+), niet gebonden aan Lifecycle, maar ondersteunt dit via stateIn(WhileSubscribed). StateFlow vereist expliciet levenscyclusbeheer in de View, maar biedt toegang tot coroutines, Flow-operators en multiplatform. Google beveelt StateFlow aan voor nieuwe Kotlin-projecten, LiveData voor Java-code of wanneer compatibiliteit met oude bibliotheken nodig is.

Hoe converteer ik LiveData naar StateFlow?

Gebruik de extensiefunctie liveData.asFlow() uit de bibliotheek lifecycle-livedata-ktx. Deze maakt een Flow aan die de huidige waarde van LiveData bij elke wijziging emitteert. Converteer vervolgens naar StateFlow via .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), initialValue). Omgekeerde conversie — stateFlow.asLiveData(). Wederzijdse conversie maakt het mogelijk de voordelen van beide bibliotheken in één project te gebruiken.

Wanneer verliest LiveData gegevens bij postValue?

postValue() gebruikt AtomicReference voor het opslaan van de uitgestelde waarde. Als postValue() twee keer wordt aangeroepen vóór verwerking door de hoofdthread, wordt de eerste waarde overschreven door de tweede — Observable ontvangt alleen de laatste. Dit komt doordat LiveData geen interne wachtrij heeft: het slaat slechts één uitgestelde waarde op. Voor het doorgeven van elk tussenliggend punt (1%, 2%, … 100%) gebruikt u setValue() op de hoofdthread of ConflatedFlow uit kotlinx-coroutines.

Kan ik LiveData zonder LifecycleOwner gebruiken?

Ja, LiveData kan worden geobserveerd via observeForever(), door Observer zonder LifecycleOwner door te geven. In dit geval moet uitschrijving echter expliciet zijn via removeObserver() — automatische uitschrijving werkt niet. observeForever() wordt toegepast in services, ContentProvider of ViewModel waar LifecycleOwner niet beschikbaar is. Volgens Google's aanbeveling vermijd observeForever() in Activity/Fragment — gebruik observe() met LifecycleOwner.

Wat is LiveData versie 1.0 (altijd actueel)?

Gedragskenmerk: wanneer LiveData een nieuwe actieve Observer ontvangt, krijgt deze onmiddellijk de laatste waarde (indien ingesteld). Oude versies van LiveData (vóór lifecycle 2.5.0) leverden de waarde zelfs aan inactive abonnees bij overgang naar actieve toestand — dit is opgelost. In de huidige versie ontvangt LiveData de laatste waarde bij overgang VAN inactive NAAR actieve toestand, wat initialisatie van schermen vereenvoudigt.

Samenvatting

  • LiveData — waarneembare gegevenscontainer met automatische koppeling aan Lifecycle, elimineert geheugenlekken en crashes door verouderde verwijzingen.
  • MutableLiveData met setValue() (hoofdthread) en postValue() (achtergrondthread) — primaire API voor gegevenswijziging.
  • Transformaties map(), switchMap() en MediatorLiveData — functionele ketens zonder boilerplate-code.
  • liveData builder liveData { } — coroutine-benadering voor het maken van asynchrone LiveData met automatische annulering van coroutines.
  • Room + LiveData — kant-en-klare combinatie voor lokale caching zonder extra code in DAO.
  • LiveData wordt gebruikt in 74% van de Jetpack-projecten en blijft de standaard voor Java-code en legacy-architecturen.
  • Voor nieuwe Kotlin-projecten beveelt Google StateFlow aan, maar LiveData blijft een compatibele oplossing voor hybride stacks.

We ontwikkelen een mobiele applicatie turnkey

IT Sectr creëert sinds 2017 iOS- en Android-applicaties voor startups en bedrijven. We adviseren u en stellen de beste oplossing voor.

Bespreek het project

Lees ook