Der Status in einer App ist jeder Wert, der sich im Laufe der Zeit ändern kann. Das ist eine sehr allgemeine Definition, die alles von einer Room-Datenbank bis hin zu einer Variablen in einer Klasse umfasst.
Alle Android-Apps zeigen dem Nutzer den Status an. Einige Beispiele für den Status in Android-Apps:
- Eine Snackbar, die angezeigt wird, wenn keine Netzwerkverbindung hergestellt werden kann.
- Ein Blogpost und die zugehörigen Kommentare.
- Wellenanimationen auf Schaltflächen, die abgespielt werden, wenn ein Nutzer darauf klickt.
- Sticker, die ein Nutzer auf ein Bild zeichnen kann.
Mit Jetpack Compose können Sie explizit angeben, wo und wie Sie den Status in einer Android-App speichern und verwenden. In diesem Leitfaden geht es um die Verbindung zwischen Status und Composables sowie um die APIs, die Jetpack Compose bietet, um einfacher mit dem Status zu arbeiten.
Zustand und Zusammensetzung
Compose ist deklarativ. Die einzige Möglichkeit, es zu aktualisieren, besteht darin, dasselbe Composable mit neuen Argumenten aufzurufen. Diese Argumente stellen den UI-Status dar. Jedes Mal, wenn ein Status aktualisiert wird, findet eine Neuzusammensetzung statt. Daher werden Dinge wie TextField
nicht automatisch aktualisiert wie in imperativen XML-basierten Ansichten. Ein Composable muss explizit über den neuen Status informiert werden, damit es entsprechend aktualisiert wird.
@Composable private fun HelloContent() { Column(modifier = Modifier.padding(16.dp)) { Text( text = "Hello!", modifier = Modifier.padding(bottom = 8.dp), style = MaterialTheme.typography.bodyMedium ) OutlinedTextField( value = "", onValueChange = { }, label = { Text("Name") } ) } }
Wenn Sie diesen Code ausführen und versuchen, Text einzugeben, werden Sie feststellen, dass nichts passiert. Das liegt daran, dass TextField
nicht automatisch aktualisiert wird, sondern nur, wenn sich der Parameter value
ändert. Das liegt daran, wie Komposition und Neukomposition in Compose funktionieren.
Weitere Informationen zur ersten Komposition und zur Neukomposition finden Sie unter Thinking in Compose.
Status in komponierbaren Funktionen
Composable-Funktionen können die remember
-API verwenden, um ein Objekt im Arbeitsspeicher zu speichern. Ein von remember
berechneter Wert wird während der ersten Komposition in der Komposition gespeichert und der gespeicherte Wert wird während der Neukomposition zurückgegeben.
remember
kann zum Speichern von veränderlichen und unveränderlichen Objekten verwendet werden.
mutableStateOf
erstellt ein Observable MutableState<T>
, das ein in die Compose-Laufzeit integrierter Observable-Typ ist.
interface MutableState<T> : State<T> {
override var value: T
}
Alle Änderungen an value
führen zu einer Neuzusammenstellung aller zusammensetzbaren Funktionen, die value
lesen.
Es gibt drei Möglichkeiten, ein MutableState
-Objekt in einer Composable-Funktion zu deklarieren:
val mutableState = remember { mutableStateOf(default) }
var value by remember { mutableStateOf(default) }
val (value, setValue) = remember { mutableStateOf(default) }
Diese Deklarationen sind gleichwertig und dienen als syntaktischer Zucker für verschiedene Verwendungszwecke von Status. Sie sollten die Option auswählen, die den am einfachsten zu lesenden Code in der Composable-Funktion erzeugt, die Sie schreiben.
Für die by
-Delegat-Syntax sind die folgenden Importe erforderlich:
import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue
Sie können den gespeicherten Wert als Parameter für andere Composables oder sogar als Logik in Anweisungen verwenden, um zu ändern, welche Composables angezeigt werden. Wenn Sie beispielsweise die Begrüßung nicht anzeigen möchten, wenn der Name leer ist, verwenden Sie den Status in einer if
-Anweisung:
@Composable fun HelloContent() { Column(modifier = Modifier.padding(16.dp)) { var name by remember { mutableStateOf("") } if (name.isNotEmpty()) { Text( text = "Hello, $name!", modifier = Modifier.padding(bottom = 8.dp), style = MaterialTheme.typography.bodyMedium ) } OutlinedTextField( value = name, onValueChange = { name = it }, label = { Text("Name") } ) } }
Mit remember
können Sie den Status bei Recompositionen beibehalten, aber nicht bei Konfigurationsänderungen. Dazu müssen Sie rememberSaveable
verwenden. rememberSaveable
speichert automatisch alle Werte, die in einem Bundle
gespeichert werden können. Für andere Werte können Sie ein benutzerdefiniertes Saver-Objekt übergeben.
Andere unterstützte Arten von Status
In Compose müssen Sie MutableState<T>
nicht verwenden, um den Status zu speichern. Es werden auch andere beobachtbare Typen unterstützt. Bevor Sie einen anderen beobachtbaren Typ in Compose lesen, müssen Sie ihn in einen State<T>
konvertieren, damit Composables automatisch neu zusammengesetzt werden können, wenn sich der Status ändert.
Compose bietet Funktionen zum Erstellen von State<T>
aus gängigen beobachtbaren Typen, die in Android-Apps verwendet werden. Bevor Sie diese Integrationen verwenden, müssen Sie die entsprechenden Artefakte wie unten beschrieben hinzufügen:
Flow
:collectAsStateWithLifecycle()
Mit
collectAsStateWithLifecycle()
werden Werte aus einemFlow
auf lebenszyklusbewusste Weise erfasst, sodass Ihre App Ressourcen sparen kann. Sie stellt den zuletzt ausgegebenen Wert aus dem Compose-State
dar. Diese API ist die empfohlene Methode zum Erfassen von Abläufen in Android-Apps.Die folgende dependency ist in der Datei
build.gradle
erforderlich (sie sollte 2.6.0-beta01 oder höher sein):
Kotlin
dependencies {
...
implementation("androidx.lifecycle:lifecycle-runtime-compose:2.8.7")
}
Groovy
dependencies {
...
implementation "androidx.lifecycle:lifecycle-runtime-compose:2.8.7"
}
-
collectAsState
ähneltcollectAsStateWithLifecycle
, da auch hier Werte aus einemFlow
erfasst und in ComposeState
umgewandelt werden.Verwenden Sie
collectAsState
für plattformunabhängigen Code anstelle voncollectAsStateWithLifecycle
, das nur für Android gilt.Für
collectAsState
sind keine zusätzlichen Abhängigkeiten erforderlich, da es incompose-runtime
verfügbar ist. -
observeAsState()
beginnt mit der Beobachtung vonLiveData
und stellt die Werte überState
dar.Die folgende Abhängigkeit ist in der Datei
build.gradle
erforderlich:
Kotlin
dependencies {
...
implementation("androidx.compose.runtime:runtime-livedata:1.8.1")
}
Groovy
dependencies {
...
implementation "androidx.compose.runtime:runtime-livedata:1.8.1"
}
-
subscribeAsState()
sind Erweiterungsfunktionen, die reaktive Streams von RxJava2 (z.B.Single
,Observable
,Completable
) in Compose-State
umwandeln.Die folgende Abhängigkeit ist in der Datei
build.gradle
erforderlich:
Kotlin
dependencies {
...
implementation("androidx.compose.runtime:runtime-rxjava2:1.8.1")
}
Groovy
dependencies {
...
implementation "androidx.compose.runtime:runtime-rxjava2:1.8.1"
}
-
subscribeAsState()
sind Erweiterungsfunktionen, die reaktive Streams von RxJava3 (z.B.Single
,Observable
,Completable
) in Compose-State
umwandeln.Die folgende Abhängigkeit ist in der Datei
build.gradle
erforderlich:
Kotlin
dependencies {
...
implementation("androidx.compose.runtime:runtime-rxjava3:1.8.1")
}
Groovy
dependencies {
...
implementation "androidx.compose.runtime:runtime-rxjava3:1.8.1"
}
Zustandsorientiert im Vergleich zu zustandslos
Ein Composable, das remember
zum Speichern eines Objekts verwendet, erstellt einen internen Status und macht das Composable zustandsbehaftet. HelloContent
ist ein Beispiel für eine zustandsbehaftete Composable, da sie ihren name
-Zustand intern speichert und ändert. Das kann in Situationen nützlich sein, in denen ein Aufrufer den Status nicht steuern muss und ihn verwenden kann, ohne den Status selbst verwalten zu müssen. Composables mit internem Status sind jedoch in der Regel weniger wiederverwendbar und schwieriger zu testen.
Eine zustandslose Composable ist eine Composable, die keinen Status enthält. Eine einfache Möglichkeit, Zustandsfreiheit zu erreichen, ist die Verwendung von State Hoisting.
Wenn Sie wiederverwendbare Composables entwickeln, möchten Sie oft sowohl eine zustandsorientierte als auch eine zustandslose Version desselben Composables bereitstellen. Die zustandsorientierte Version ist praktisch für Aufrufer, die sich nicht für den Zustand interessieren, und die zustandslose Version ist für Aufrufer erforderlich, die den Zustand steuern oder anheben müssen.
State Hoisting
State Hoisting in Compose ist ein Muster, bei dem der Status an den Aufrufer eines Composables übergeben wird, um das Composable zustandslos zu machen. Das allgemeine Muster für das State Hoisting in Jetpack Compose besteht darin, die Statusvariable durch zwei Parameter zu ersetzen:
value: T
:der aktuelle anzuzeigende WertonValueChange: (T) -> Unit
:Ein Ereignis, das eine Änderung des Werts anfordert, wobeiT
der vorgeschlagene neue Wert ist.
Sie sind jedoch nicht auf onValueChange
beschränkt. Wenn spezifischere Ereignisse für die Composable infrage kommen, sollten Sie sie mit Lambdas definieren.
Der so hochgestufte Status hat einige wichtige Eigenschaften:
- Single Source of Truth:Da wir den Status verschieben, anstatt ihn zu duplizieren, gibt es nur eine einzige Quelle der Wahrheit. So lassen sich Fehler vermeiden.
- Gekapselt:Nur zustandsorientierte Composables können ihren Zustand ändern. Sie ist vollständig intern.
- Freigabefähig:Der angehobene Status kann für mehrere Composables freigegeben werden. Wenn Sie
name
in einem anderen Composable lesen möchten, ist das durch Hoisting möglich. - Abfangbar:Aufrufer der zustandslosen Composables können Ereignisse ignorieren oder ändern, bevor sie den Status ändern.
- Entkoppelt:Der Status für die zustandslosen Composables kann überall gespeichert werden. So ist es jetzt beispielsweise möglich,
name
in einViewModel
zu verschieben.
Im Beispiel extrahieren Sie name
und onValueChange
aus HelloContent
und verschieben sie im Baum nach oben zu einer HelloScreen
-Composable, die HelloContent
aufruft.
@Composable fun HelloScreen() { var name by rememberSaveable { mutableStateOf("") } HelloContent(name = name, onNameChange = { name = it }) } @Composable fun HelloContent(name: String, onNameChange: (String) -> Unit) { Column(modifier = Modifier.padding(16.dp)) { Text( text = "Hello, $name", modifier = Modifier.padding(bottom = 8.dp), style = MaterialTheme.typography.bodyMedium ) OutlinedTextField(value = name, onValueChange = onNameChange, label = { Text("Name") }) } }
Wenn Sie den Status aus HelloContent
herausziehen, ist es einfacher, die Composable zu verstehen, sie in verschiedenen Situationen wiederzuverwenden und zu testen. HelloContent
ist unabhängig davon, wie der Status gespeichert wird. Durch die Entkopplung müssen Sie die Implementierung von HelloContent
nicht ändern, wenn Sie HelloScreen
ändern oder ersetzen.

Das Muster, bei dem der Status sinkt und die Ereignisse steigen, wird als unidirektionaler Datenfluss bezeichnet. In diesem Fall sinkt der Status von HelloScreen
auf HelloContent
und die Ereignisse steigen von HelloContent
auf HelloScreen
. Durch die Verwendung eines unidirektionalen Datenflusses können Sie Composables, die den Status in der Benutzeroberfläche anzeigen, von den Teilen Ihrer App entkoppeln, in denen der Status gespeichert und geändert wird.
Weitere Informationen finden Sie auf der Seite Where to hoist state.
Zustand in Compose wiederherstellen
Die rememberSaveable
API verhält sich ähnlich wie remember
, da der Status über Re-Compositions und auch über die Neuerstellung von Aktivitäten oder Prozessen mithilfe des Mechanismus für den gespeicherten Instanzstatus hinweg beibehalten wird. Das passiert beispielsweise, wenn der Bildschirm gedreht wird.
Möglichkeiten zum Speichern des Status
Alle Datentypen, die Bundle
hinzugefügt werden, werden automatisch gespeichert. Wenn Sie etwas speichern möchten, das nicht in die Bundle
aufgenommen werden kann, haben Sie mehrere Möglichkeiten.
In Pakete aufteilen
Die einfachste Lösung ist, dem Objekt die Annotation @Parcelize
hinzuzufügen. Das Objekt wird in ein Paket umgewandelt und kann gebündelt werden. Mit diesem Code wird beispielsweise ein Parcelable-Datentyp City
erstellt und im Status gespeichert.
@Parcelize data class City(val name: String, val country: String) : Parcelable @Composable fun CityScreen() { var selectedCity = rememberSaveable { mutableStateOf(City("Madrid", "Spain")) } }
MapSaver
Wenn @Parcelize
aus irgendeinem Grund nicht geeignet ist, können Sie mit mapSaver
eine eigene Regel zum Konvertieren eines Objekts in eine Reihe von Werten definieren, die das System in Bundle
speichern kann.
data class City(val name: String, val country: String) val CitySaver = run { val nameKey = "Name" val countryKey = "Country" mapSaver( save = { mapOf(nameKey to it.name, countryKey to it.country) }, restore = { City(it[nameKey] as String, it[countryKey] as String) } ) } @Composable fun CityScreen() { var selectedCity = rememberSaveable(stateSaver = CitySaver) { mutableStateOf(City("Madrid", "Spain")) } }
ListSaver
Um die Schlüssel für die Karte nicht definieren zu müssen, können Sie auch listSaver
verwenden und die zugehörigen Indexe als Schlüssel nutzen:
data class City(val name: String, val country: String) val CitySaver = listSaver<City, Any>( save = { listOf(it.name, it.country) }, restore = { City(it[0] as String, it[1] as String) } ) @Composable fun CityScreen() { var selectedCity = rememberSaveable(stateSaver = CitySaver) { mutableStateOf(City("Madrid", "Spain")) } }
State Holder in Compose
Einfaches State Hoisting kann in den zusammensetzbaren Funktionen selbst verwaltet werden. Wenn jedoch die Menge des zu verfolgenden Status zunimmt oder die Logik für die Ausführung in zusammensetzbaren Funktionen entsteht, ist es ratsam, die Verantwortlichkeiten für Logik und Status an andere Klassen zu delegieren: State-Holder.
Weitere Informationen finden Sie in der Dokumentation zu State Hoisting in Compose oder auf der Seite State Holders und UI-Status im Architekturleitfaden.
Berechnungen für Erinnerungen neu auslösen, wenn sich Schlüssel ändern
Die remember
API wird häufig zusammen mit MutableState
verwendet:
var name by remember { mutableStateOf("") }
Hier sorgt die Verwendung der Funktion remember
dafür, dass der MutableState
-Wert Recompositions übersteht.
Im Allgemeinen wird für remember
ein calculation
-Lambda-Parameter verwendet. Wenn remember
zum ersten Mal ausgeführt wird, wird die Lambda-Funktion calculation
aufgerufen und ihr Ergebnis gespeichert. Während der Neuzusammenstellung gibt remember
den zuletzt gespeicherten Wert zurück.
Neben dem Zwischenspeichern von Status können Sie mit remember
auch beliebige Objekte oder Ergebnisse eines Vorgangs in der Komposition speichern, deren Initialisierung oder Berechnung aufwendig ist. Möglicherweise möchten Sie diese Berechnung nicht bei jeder Neuzusammenstellung wiederholen.
Ein Beispiel ist das Erstellen dieses ShaderBrush
-Objekts, das ein aufwendiger Vorgang ist:
val brush = remember { ShaderBrush( BitmapShader( ImageBitmap.imageResource(res, avatarRes).asAndroidBitmap(), Shader.TileMode.REPEAT, Shader.TileMode.REPEAT ) ) }
In remember
wird der Wert gespeichert, bis er die Komposition verlässt. Es gibt jedoch eine Möglichkeit, den im Cache gespeicherten Wert zu entwerten. Die remember
API akzeptiert auch einen key
- oder keys
-Parameter. Wenn sich einer dieser Schlüssel ändert, wird der Cache beim nächsten Mal, wenn die Funktion neu zusammengesetzt wird, durch remember
ungültig gemacht und der Lambda-Block für die Berechnung wird noch einmal ausgeführt. Mit diesem Mechanismus können Sie die Lebensdauer eines Objekts in der Komposition steuern. Die Berechnung bleibt gültig, bis sich die Eingaben ändern, und nicht bis der gespeicherte Wert die Komposition verlässt.
Die folgenden Beispiele zeigen, wie dieser Mechanismus funktioniert.
In diesem Snippet wird ein ShaderBrush
erstellt und als Hintergrundfarbe eines Box
-Composable verwendet. In remember
wird die ShaderBrush
-Instanz gespeichert, da das Neuerstellen, wie bereits erläutert, aufwendig ist. remember
verwendet avatarRes
als key1
-Parameter, also das ausgewählte Hintergrundbild. Wenn sich avatarRes
ändert, wird der Pinsel mit dem neuen Bild neu zusammengesetzt und auf Box
angewendet. Das kann passieren, wenn der Nutzer in einer Auswahl ein anderes Bild als Hintergrund auswählt.
@Composable private fun BackgroundBanner( @DrawableRes avatarRes: Int, modifier: Modifier = Modifier, res: Resources = LocalContext.current.resources ) { val brush = remember(key1 = avatarRes) { ShaderBrush( BitmapShader( ImageBitmap.imageResource(res, avatarRes).asAndroidBitmap(), Shader.TileMode.REPEAT, Shader.TileMode.REPEAT ) ) } Box( modifier = modifier.background(brush) ) { /* ... */ } }
Im nächsten Snippet wird der Status in eine einfache Status-Holder-Klasse
MyAppState
verschoben. Sie stellt eine rememberMyAppState
-Funktion zum Initialisieren einer Instanz der Klasse mit remember
bereit. Das Bereitstellen solcher Funktionen zum Erstellen einer Instanz, die Recompositions übersteht, ist ein gängiges Muster in Compose. Die Funktion rememberMyAppState
empfängt windowSizeClass
, das als key
-Parameter für remember
dient. Wenn sich dieser Parameter ändert, muss die App die einfache Status-Holder-Klasse mit dem neuesten Wert neu erstellen. Das kann beispielsweise passieren, wenn der Nutzer das Gerät dreht.
@Composable private fun rememberMyAppState( windowSizeClass: WindowSizeClass ): MyAppState { return remember(windowSizeClass) { MyAppState(windowSizeClass) } } @Stable class MyAppState( private val windowSizeClass: WindowSizeClass ) { /* ... */ }
Compose verwendet die equals-Implementierung der Klasse, um zu entscheiden, ob sich ein Schlüssel geändert hat, und um den gespeicherten Wert zu invalidieren.
Status mit Schlüsseln über die Neuzusammenstellung hinaus speichern
Die rememberSaveable
API ist ein Wrapper für remember
, mit dem Daten in einer Bundle
gespeichert werden können. Mit dieser API kann der Status nicht nur die Neuzusammensetzung, sondern auch die Neuerstellung von Aktivitäten und das vom System initiierte Beenden von Prozessen überdauern.
rememberSaveable
empfängt input
-Parameter für denselben Zweck, für den remember
keys
empfängt. Der Cache wird ungültig, wenn sich eine der Eingaben ändert. Beim nächsten Mal, wenn die Funktion neu zusammengesetzt wird, wird der Lambda-Block für die Berechnung von rememberSaveable
noch einmal ausgeführt.
Im folgenden Beispiel wird userTypedQuery
in rememberSaveable
gespeichert, bis sich typedQuery
ändert:
var userTypedQuery by rememberSaveable(typedQuery, stateSaver = TextFieldValue.Saver) { mutableStateOf( TextFieldValue(text = typedQuery, selection = TextRange(typedQuery.length)) ) }
Weitere Informationen
Weitere Informationen zu State und Jetpack Compose finden Sie in den folgenden zusätzlichen Ressourcen.
Produktproben
Codelabs
Videos
Blogs
Empfehlungen für dich
- Hinweis: Linktext wird angezeigt, wenn JavaScript deaktiviert ist.
- Compose-UI entwerfen
- UI-Status in Compose speichern
- Nebeneffekte in Compose