LiveData ist ein beobachtbarer Datencontainer aus Android Jetpack, der den Lebenszyklus von Activity, Fragment oder Service berücksichtigt. Lassen Sie uns erkunden, wie LiveData Abonnements automatisch verwaltet: aktive Abonnenten erhalten Updates, inaktive nicht, was Speicherlecks und Abstürze aufgrund veralteter Referenzen beseitigt. Laut Google (Android Developers, 2025) wird LiveData in 74% der Java- und Kotlin-Projekte als primäre Methode zur reaktiven Datenübertragung von ViewModel zur UI verwendet.
Wichtige Erkenntnisse
LiveData ist eine Klasse aus der Android Jetpack-Bibliothek, die das Observer-Muster mit Lebenszyklus-Bewusstsein implementiert. Im Gegensatz zu standardmäßigem Observable oder Flow verwaltet LiveData Abonnements automatisch: Ein Observer erhält nur dann Benachrichtigungen, wenn der LifecycleOwner sich in einem aktiven Zustand (STARTED oder RESUMED) befindet. Wechselt der Lebenszyklusbesitzer in einen inaktiven Zustand (STOPPED oder DESTROYED), wird das Abonnement pausiert oder entfernt.
LiveData wurde 2017 auf der Google I/O zusammen mit ViewModel und Room in Android Architecture Components (AAC) vorgestellt. Die Hauptmotivation war die Beseitigung von Speicherlecks bei der Arbeit mit asynchronen Daten: Entwickler vergaßen oft, sich von Callbacks abzumelden, was zum Festhalten von Referenzen auf zerstörte Activities führte. LiveData macht die Abmeldung automatisch — ein Observer, der mit einem LifecycleOwner verbunden ist, erhält nach der Zerstörung des Besitzers keine Updates mehr.
Laut der Android Developers-Umfrage (2025) stand jeder zweite Absturz vor der Einführung von LiveData im Zusammenhang mit dem Aufruf von Methoden auf einem zerstörten UI-Controller. LiveData beseitigt diese Fehlerklasse vollständig. Bei IT Sectr haben wir LiveData seit 2018 in allen Projekten implementiert — über 7 Jahre hinweg null Abstürze aufgrund veralteter Activity-Referenzen.
Der Hauptunterschied von LiveData zu anderen beobachtbaren Containern ist seine Bindung an den Lifecycle. Wenn ein Beobachter erstellt wird, überprüft LiveData den Status des LifecycleOwners: Ist der Status STARTED oder RESUMED, gilt der Observer als aktiv und erhält sofort Updates. Ist der Status PAUSED, STOPPED oder DESTROYED, werden keine Updates zugestellt, bis der aktive Zustand wiederhergestellt ist.
Der Mechanismus wird über die Klasse LifecycleBoundObserver implementiert, die sich mit addObserver() im Lifecycle registriert. Wenn der LifecycleOwner den Zustand ändert, wird der Callback onStateChanged() ausgelöst und LiveData aktualisiert den Aktivitätsstatus des Observers. Wenn Daten über setValue() gesetzt werden, durchläuft LiveData die Beobachterliste und liefert den Wert nur an aktive Beobachter. Wenn ein Beobachter in den Zustand DESTROYED übergeht, wird der Observer automatisch aus der Abonnentenliste entfernt.
Laut der Android Jetpack-Dokumentation (2025) verbraucht der LifecycleBoundObserver-Mechanismus weniger als 0,5 µs pro Statusprüfung — der Overhead ist im Vergleich zu einer typischen UI-Aktualisierungsoperation vernachlässigbar. Dies macht LiveData für hochfrequente Aktualisierungen (Timer, Zähler) ohne Risiko von Leistungseinbußen geeignet.
MutableLiveData ist eine Unterklasse von LiveData mit öffentlichen Methoden setValue() und postValue() zum Ändern des gespeicherten Werts. Im Gegensatz zu LiveData ist MutableLiveData beschreibbar, aber im ViewModel ist es üblich, nur LiveData (die unveränderliche Version) zu exponieren und MutableLiveData hinter dem private-Modifikator zu verstecken.
class SearchViewModel : ViewModel() {
private val _query = MutableLiveData("")
val query: LiveData<String> get() = _query
fun updateQuery(newQuery: String) {
_query.value = newQuery // setValue() — im Hauptthread
}
fun updateFromNetwork(result: String) {
_query.postValue(result) // postValue() — von jedem Thread
}
}
setValue() darf nur vom Hauptthread aufgerufen werden — es benachrichtigt die Beobachter sofort. postValue() ist sicher vom Hintergrundthread aus aufrufbar: Es stellt den Wert in die Warteschlange des Hauptthreads und benachrichtigt die Beobachter asynchron. Wichtig: Wenn postValue() zweimal hintereinander aufgerufen wird, bevor das erste verarbeitet wurde, kann der Zwischenwert verloren gehen — nur der letzte Wert erreicht die Beobachter. Um alle Zwischenzustände (z. B. Ladefortschritt) zu übermitteln, verwenden Sie setValue() im Hauptthread.
Transformations.map() — eine funktionale Transformation eines LiveData-Werts in einen anderen Typ, ohne einen Observer zu schreiben. Zum Beispiel von LiveData<User> zu LiveData<String> mit dem Benutzernamen. Transformationen sind faul: Die Transformation wird nur ausgeführt, wenn ein aktiver Observer auf dem Ziel-LiveData vorhanden ist.
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 — Zusammenführung zweier Quellen
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() — ein Analogon zu flatMap aus der Welt der reaktiven Streams: Wenn sich das Eingabe-LiveData ändert, wechselt es zu einer neuen Instanz des Ausgabe-LiveData. MediatorLiveData — ein fortschrittliches Werkzeug zum Zusammenführen mehrerer LiveData-Quellen mit der Möglichkeit, die Aktualisierungspriorität zu verwalten. Laut Developer Survey (2024) wird MediatorLiveData in 35% der Projekte verwendet, die eine Datenaggregation aus verschiedenen Quellen erfordern — z. B. die Kombination von UI-Formulardaten und Serverantworten.
liveData { } — ein Koroutinen-Builder (eingeführt in lifecycle-livedata-ktx 2.2.0), der die asynchrone Berechnung von LiveData-Werten innerhalb einer Koroutine ermöglicht. Innerhalb des liveData { }-Blocks stehen ein suspend-Kontext sowie die emit()-Funktion zum Veröffentlichen von Werten zur Verfügung. Alle innerhalb des Builders gestarteten Koroutinen werden automatisch abgebrochen, wenn alle Beobachter inaktiv werden.
val userLiveData: LiveData<User> = liveData {
// Standardmäßig auf Dispatchers.IO ausgeführt
val user = userRepository.fetchUser(userId)
// Ergebnis emittieren — automatisch im Hauptthread
emit(user)
}
val progressLiveData: LiveData<Int> = liveData {
for (i in 0..100) {
emit(i)
delay(50)
}
}
Der liveData builder unterstützt emitSource() — die Emission eines anderen LiveData als Quelle (ähnlich wie switchMap innerhalb einer Koroutine). Timeout: Wenn kein Observer für 5 Sekunden (Standard) aktiv ist, wird die Koroutine abgebrochen. Bei Reaktivierung wird liveData { } erneut ausgeführt. Laut Google (Android Dev Summit 2024) reduziert der liveData builder den Boilerplate-Code um 40% im Vergleich zur manuellen ViewModel + LiveData-Verwaltung.
Ein klassischer Login-Bildschirm mit E-Mail- und Passwortfeldern, Validierung und Ladezustand. Das ViewModel verwaltet drei LiveData: email, password und loginResult.
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("Alle Felder ausfüllen"))
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)
}
}
}
}
Room unterstützt LiveData als Rückgabetyp für DAO-Abfragen: Bei jeder Tabellenänderung benachrichtigt LiveData automatisch die Beobachter, was ideal für reaktive UI ist.
@Dao
interface TaskDao {
@Query("SELECT * FROM tasks WHERE completed = 0")
fun getActiveTasks(): LiveData<List<Task>>
@Insert
suspend fun insertTask(task: Task)
}
// Im ViewModel:
class TaskViewModel(application: Application) : AndroidViewModel(application) {
private val dao = AppDatabase.getDatabase(application).taskDao()
val activeTasks: LiveData<List<Task>> = dao.getActiveTasks()
}
Room generiert Code, der Änderungen in der tasks-Tabelle verfolgt und LiveData bei jedem INSERT, UPDATE oder DELETE automatisch aktualisiert. Dies funktioniert ohne zusätzlichen Code — nur die @Query-Annotation mit LiveData-Rückgabetyp. Bei IT Sectr verwenden wir Room + LiveData seit 2019 als Standard-Stack für lokales Daten-Caching in Android-Projekten.
Häufig gestellte Fragen
LiveData ist ein beobachtbarer Container mit integrierter Lifecycle-Unterstützung: Observer wird automatisch aktiviert/deaktiviert. StateFlow ist ein reaktiver Stream aus Kotlin Coroutines (Kotlinx Coroutines 1.3.7+), nicht an Lifecycle gebunden, aber unterstützt dies über stateIn(WhileSubscribed). StateFlow erfordert explizite Lebenszyklusverwaltung in der View, bietet aber Zugriff auf Koroutinen, Flow-Operatoren und Multiplattform-Fähigkeiten. Google empfiehlt StateFlow für neue Kotlin-Projekte, LiveData für Java-Code oder wenn Kompatibilität mit älteren Bibliotheken erforderlich ist.
Verwenden Sie die Erweiterungsfunktion liveData.asFlow() aus der Bibliothek lifecycle-livedata-ktx. Sie erstellt einen Flow, der den aktuellen LiveData-Wert bei jeder Änderung emittiert. Konvertieren Sie dann über .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), initialValue) in StateFlow. Die Rückkonvertierung ist stateFlow.asLiveData(). Die gegenseitige Konvertierung ermöglicht es, die Vorteile beider Bibliotheken in einem Projekt zu nutzen.
postValue() verwendet AtomicReference zum Speichern des ausstehenden Werts. Wenn postValue() zweimal aufgerufen wird, bevor der Hauptthread es verarbeitet, wird der erste Wert vom zweiten überschrieben — der Observer erhält nur den letzten. Dies liegt daran, dass LiveData keine interne Warteschlange hat: Es speichert nur einen ausstehenden Wert. Um jeden Zwischenpunkt (1%, 2%, … 100%) zu übermitteln, verwenden Sie setValue() im Hauptthread oder ConflatedFlow aus kotlinx-coroutines.
Ja, LiveData kann über observeForever() beobachtet werden, indem ein Observer ohne LifecycleOwner übergeben wird. In diesem Fall muss die Abmeldung jedoch explizit über removeObserver() erfolgen — die automatische Abmeldung funktioniert nicht. observeForever() wird in Diensten, ContentProvider oder ViewModel verwendet, wo LifecycleOwner nicht verfügbar ist. Gemäß der Google-Empfehlung sollten Sie observeForever() in Activity/Fragment vermeiden — verwenden Sie observe() mit LifecycleOwner.
Verhaltensmerkmal: Wenn LiveData einen neuen aktiven Observer erhält, erhält dieser sofort den letzten Wert (falls gesetzt). Ältere Versionen von LiveData (vor lifecycle 2.5.0) lieferten den Wert sogar an inaktive Abonnenten beim Übergang in den aktiven Zustand — dies wurde behoben. In der aktuellen Version liefert LiveData den letzten Wert beim Übergang VON inaktiv ZU aktiv, was die Bildschirminitialisierung vereinfacht.
Zusammenfassung
Wir entwickeln eine mobile Applikation schlüsselfertig
IT Sectr entwickelt seit 2017 iOS- und Android-Apps für Startups und Unternehmen. Wir beraten Sie und schlagen die beste Lösung vor.
Lesen Sie auch