Jetpack Compose: Zustand sichern mit rememberSaveable
Erfahre, wie du mit rememberSaveable UI-Zustände über Konfigurationsänderungen und Prozessneustarts hinweg in Jetpack Compose zuverlässig erhältst.
Stell dir vor, dein Nutzer füllt ein längeres Formular in deiner Android-App aus, dreht das Smartphone für eine breitere Tastaturansicht und plötzlich sind sämtliche Eingaben verschwunden. Dieses Verhalten ist ein klassisches und oft frustrierendes Problem in der Entwicklung mobiler Anwendungen. In der modernen Welt von Jetpack Compose löst du diese Herausforderung sicher, effizient und mit wenig Code durch den gezielten Einsatz von rememberSaveable.
Was ist das?
Jetpack Compose arbeitet nach einem deklarativen Prinzip. Das bedeutet, dass die Benutzeroberfläche bei relevanten Statusänderungen kontinuierlich neu gezeichnet wird. Dieser fundamentale Prozess wird als Recomposition bezeichnet. Wenn du eine Variable in deiner UI lediglich mit der Funktion remember speicherst, bleibt der Wert zwar während dieser standardmäßigen Recomposition erhalten, überlebt aber keine tiefgreifenden Systemereignisse.
Sobald das Android-Betriebssystem die aktuelle Activity neu erstellt – etwa bei einer Bildschirmdrehung (Configuration Change) oder wenn die App im Hintergrund wegen Speichermangel temporär beendet wird (Process Death) – geht ein reiner remember-Zustand unwiderruflich verloren. Genau hier greift rememberSaveable ein. Es ist eine spezielle Composable-Funktion, die deinen UI-Zustand nicht nur im flüchtigen Arbeitsspeicher der aktuellen Compose-Hierarchie hält. Stattdessen verknüpft es die Daten direkt mit dem etablierten Saved-State-Mechanismus von Android. Der Wert wird sicher verpackt und vom System aufbewahrt, bis die Benutzeroberfläche nach der Unterbrechung wieder vollständig aufgebaut wird. Dies ist ein essenzieller Baustein, um Nutzereingaben, Scrollpositionen oder den Status von Menüs exakt so wiederherzustellen, wie der Nutzer sie zuvor verlassen hat.
Wie funktioniert es?
Die technische Mechanik hinter rememberSaveable stützt sich auf das bewährte System des Android-Betriebssystems zur Zustandsspeicherung. Lange bevor Jetpack Compose existierte, haben Entwickler die Methode onSaveInstanceState in Activities oder Fragments überschrieben. Das grundlegende Prinzip bleibt identisch: Das System verlangt, dass flüchtige Daten in ein spezielles Schlüssel-Wert-Speicherobjekt, das sogenannte Bundle, verpackt werden.
Wenn die Composable-Funktion den aktiven Bildschirm verlässt oder das System beschließt, die aktuelle Activity zu zerstören, um dringend benötigte Ressourcen freizugeben, tritt dieser Speichermechanismus in Kraft. Compose durchläuft den gesamten UI-Baum, sammelt alle mit rememberSaveable markierten Werte ein und packt sie sicher in dieses Bundle. Sobald die App wieder in den Vordergrund rückt und die Benutzeroberfläche neu gezeichnet wird, holt Compose diese Werte automatisch wieder aus dem Bundle heraus und stellt den letzten bekannten Zustand her.
Da ein Bundle tief im Betriebssystem verankert ist und Daten per Inter-Process-Communication (IPC) transportiert, kann es nicht beliebige Objekte aufnehmen. Du bist bei der direkten Nutzung von rememberSaveable auf bestimmte Datentypen beschränkt. Alle primitiven Datentypen wie Int, Boolean, Float oder String funktionieren absolut reibungslos. Auch Arrays dieser Basis-Typen sowie komplexe Objekte, die explizit das Parcelable- oder Serializable-Interface implementieren, werden vom System anstandslos akzeptiert. Das System weiß bei diesen spezifischen Typen exakt, wie es sie serialisiert und fehlerfrei wiederherstellt. Versuchst du hingegen, einen nicht unterstützten Datentyp direkt zu übergeben, wird deine App zur Laufzeit sofort mit einer Exception abstürzen.
In der Praxis
Im Alltag eines Android-Entwicklers begegnet dir diese Funktion am häufigsten bei Formulareingaben, beim Steuern von temporären Navigationszuständen oder bei interaktiven UI-Elementen wie modalen Dialogen. Betrachten wir ein sehr greifbares Beispiel: ein einfaches Textfeld für einen Benutzernamen.
Wenn du den Zustand lediglich mit val text = remember { mutableStateOf("") } deklarierst, verlierst du den gesamten eingegebenen Text bei der nächsten Bildschirmdrehung. Die korrekte Lösung erfordert nur eine minimale, aber architektonisch entscheidende Anpassung in deinem Code:
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
@Composable
fun UserProfileInput() {
// Der Zustand überlebt Konfigurationsänderungen und Prozessneustarts
var username by rememberSaveable { mutableStateOf("") }
TextField(
value = username,
onValueChange = { username = it },
label = { Text("Benutzername") }
)
}
Oft stehst du jedoch vor der Anforderung, dass dein UI-Zustand nicht nur aus einem simplen String besteht. Nehmen wir an, du hast eine eigene Datenklasse entwickelt, die den Status eines Filters repräsentiert. Da Compose standardmäßig nicht weiß, wie es diese spezifische Klasse in ein Bundle schreiben soll, musst du einen eigenen Saver definieren. Hierfür bietet Compose Hilfsfunktionen wie den listSaver an. Dieser erlaubt es dir, dein Objekt in eine Liste von speicherbaren Grundtypen zu konvertieren und beim Neuaufbau der UI exakt aus dieser Liste wieder zusammenzusetzen.
import androidx.compose.runtime.saveable.listSaver
data class FilterState(val isActive: Boolean, val keyword: String)
val FilterStateSaver = listSaver<FilterState, Any>(
save = { listOf(it.isActive, it.keyword) },
restore = { FilterState(isActive = it[0] as Boolean, keyword = it[1] as String) }
)
@Composable
fun FilterComponent() {
var filter by rememberSaveable(stateSaver = FilterStateSaver) {
mutableStateOf(FilterState(false, ""))
}
// UI-Code, der den Filter-Status nutzt...
}
Eine häufige und sehr gefährliche Stolperfalle ist die Vermischung von echter Geschäftslogik und reinem UI-Zustand. Nutze rememberSaveable ausschließlich für temporäre Zustände, die primär für die visuelle Darstellung relevant sind. Speichere in diesem Konstrukt niemals große Datenmengen, lange abgerufene Listen aus einer lokalen Datenbank oder komplexe Business-Objekte. Solche Daten gehören strukturell zwingend in ein ViewModel. Das ViewModel ist exakt dafür konzipiert, den Lebenszyklus von Daten getrennt von der Benutzeroberfläche zu verwalten. Zudem hat das Bundle, auf dem rememberSaveable basiert, ein hartes und vom System strikt überwachtes Größenlimit. Dieses Limit liegt üblicherweise bei deutlich unter einem Megabyte für die gesamte Anwendung. Wenn du dieses Limit durch ausufernde Datenstrukturen überschreitest, stürzt deine App unwiderruflich mit einer TransactionTooLargeException ab. Beschränke dich bei der UI-Zustandsspeicherung daher konsequent auf IDs, Scrollpositionen oder primitive Benutzereingaben.
Fazit
Mit rememberSaveable erhältst du ein essenzielles und performantes Werkzeug, um die Nutzererfahrung deiner Compose-App bei Konfigurationsänderungen und plötzlichen Prozess-Neustarts nahtlos und professionell zu gestalten. Um dein Verständnis abzusichern, empfiehlt sich ein direkter praktischer Test: Implementiere ein Eingabefeld, wechsle im Code zwischen remember und rememberSaveable, starte die App und drehe den Bildschirm deines Testgeräts. Für eine noch realistischere Simulation aktiviere in den Entwickleroptionen deines Android-Geräts die Einstellung “Aktivitäten nicht speichern” (Don’t keep activities). Wechsle dann kurz in eine andere App auf deinem Home-Screen und kehre wieder zurück. Wenn dein Textfeld danach immer noch den korrekten Inhalt anzeigt, hast du die grundlegende Mechanik der Zustandsspeicherung in modernen Android-Apps erfolgreich verinnerlicht.