Der Status in einer App ist ein Wert, der sich im Laufe der Zeit ändern kann. Das ist eine sehr weit gefasste Definition und umfasst alles von einer Room-Datenbank bis hin zu einer Variablen in einer Klasse.
Der Status aller Android-Apps wird dem Nutzer angezeigt. Einige Beispiele für Status in Android-Apps:
- Eine Snackbar, die angezeigt wird, wenn keine Netzwerkverbindung hergestellt werden kann.
- Ein Blogpost und zugehörige Kommentare.
- Wellenanimationen auf Schaltflächen, die abgespielt werden, wenn ein Nutzer darauf klickt.
- Sticker, die Nutzer auf ein Bild kleben können.
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 zusammensetzbaren Funktionen sowie auf die APIs, die Jetpack Compose für die einfachere Arbeit mit Statusfunktionen bietet.
Status und Zusammensetzung
Compose ist deklarativ und kann daher nur durch Aufrufen desselben Composeable mit neuen Argumenten aktualisiert werden. Diese Argumente sind Darstellungen des UI-Status. Jedes Mal, wenn ein Status aktualisiert wird, erfolgt eine Neuzusammensetzung. Daher werden Dinge wie TextField
nicht automatisch aktualisiert, wie dies in imperativen XML-basierten Ansichten der Fall ist. Ein Composeable 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 das Script ausführen und versuchen, Text einzugeben, passiert nichts. Das liegt daran, dass sich TextField
nicht selbst aktualisiert, sondern nur, wenn sich der value
-Parameter ändert. Das liegt daran, wie Komposition und Neukomposition in Compose funktionieren.
Weitere Informationen zur ersten Komposition und Neukomposition finden Sie unter Komposition als Denkweise.
Status in komponierbaren Funktionen
Zusammensetzbare Funktionen können die remember
API verwenden, um ein Objekt im Arbeitsspeicher zu speichern. Ein von remember
berechneter Wert wird bei der ersten Komposition in der Komposition gespeichert und bei der Neukomposition zurückgegeben.
remember
kann sowohl zum Speichern von veränderlichen als auch von unveränderlichen Objekten verwendet werden.
mutableStateOf
erstellt ein Observable MutableState<T>
, einen Observable-Typ, der in die Compose-Laufzeit eingebunden ist.
interface MutableState<T> : State<T> {
override var value: T
}
Bei Änderungen an value
wird die Neuzusammensetzung aller zusammensetzbaren Funktionen geplant, die value
lesen.
Es gibt drei Möglichkeiten, ein MutableState
-Objekt in einem Composeable zu deklarieren:
val mutableState = remember { mutableStateOf(default) }
var value by remember { mutableStateOf(default) }
val (value, setValue) = remember { mutableStateOf(default) }
Diese Deklarationen sind äquivalent und werden als syntaktischer Zucker für verschiedene Verwendungen von „state“ bereitgestellt. Sie sollten diejenige auswählen, die den einfach zu lesenden Code in der zusammensetzbaren Funktion erzeugt, die Sie schreiben.
Für die by
-Delegierungssyntax 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 Composeables oder sogar als Logik in Anweisungen verwenden, um zu ändern, welche Composeables angezeigt werden. Wenn Sie beispielsweise nicht möchten, dass die Begrüßung angezeigt wird, 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 Neuzusammensetzungen beibehalten, er wird jedoch nicht bei Konfigurationsänderungen beibehalten. 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 Speicherobjekt übergeben.
Andere unterstützte Statustypen
Für Compose ist es nicht erforderlich, MutableState<T>
zum Speichern des Status zu verwenden. Es werden auch andere Observable-Typen unterstützt. Bevor Sie einen anderen Observable-Typ in Compose lesen, müssen Sie ihn in einen State<T>
konvertieren, damit Composables automatisch neu erstellt werden können, wenn sich der Status ändert.
Erstellen Sie Schiffe mit Funktionen, um State<T>
aus gängigen Observable-Typen zu erstellen, die in Android-Apps verwendet werden. Bevor Sie diese Integrationen verwenden, fügen Sie die entsprechenden Artefakte hinzu, wie unten beschrieben:
Flow
:collectAsStateWithLifecycle()
collectAsStateWithLifecycle()
sammelt Werte aus einemFlow
auf nutzungsabhängige Weise, sodass Ihre App App-Ressourcen sparen kann. Es stellt den letzten ausgegebenen Wert aus der ErstellungState
dar. Verwenden Sie diese API als empfohlene Methode zum Erfassen von Abläufen in Android-Apps.In der Datei
build.gradle
ist die folgende Abhängigkeit erforderlich (Version 2.6.0-beta01 oder höher):
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 einerFlow
erfasst und in „Komponieren“State
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, dieseLiveData
zu beobachten und stellt ihre Werte überState
dar.In der Datei
build.gradle
ist die folgende Abhängigkeit erforderlich:
Kotlin
dependencies {
...
implementation("androidx.compose.runtime:runtime-livedata:1.7.8")
}
Groovy
dependencies {
...
implementation "androidx.compose.runtime:runtime-livedata:1.7.8"
}
-
subscribeAsState()
sind Erweiterungsfunktionen, die die reaktiven Streams von RxJava2 (z.B.Single
,Observable
,Completable
) in das Compose-ObjektState
umwandeln.In der Datei
build.gradle
ist die folgende Abhängigkeit erforderlich:
Kotlin
dependencies {
...
implementation("androidx.compose.runtime:runtime-rxjava2:1.7.8")
}
Cool
dependencies {
...
implementation "androidx.compose.runtime:runtime-rxjava2:1.7.8"
}
-
subscribeAsState()
sind Erweiterungsfunktionen, die die reaktiven Streams von RxJava3 (z.B.Single
,Observable
,Completable
) in ComposeState
umwandeln.In der Datei
build.gradle
ist die folgende Abhängigkeit erforderlich:
Kotlin
dependencies {
...
implementation("androidx.compose.runtime:runtime-rxjava3:1.7.8")
}
Groovy
dependencies {
...
implementation "androidx.compose.runtime:runtime-rxjava3:1.7.8"
}
Zustandsorientiert im Vergleich zu zustandslos
Ein Composeable, das ein Objekt mit remember
speichert, erstellt einen internen Status und ist somit zustandsabhängig. HelloContent
ist ein Beispiel für einen zustandsorientierten Composeable, da er seinen name
-Status intern speichert und ändert. Dies kann in Situationen nützlich sein, in denen ein Aufrufer den Status nicht steuern muss, sondern ihn verwenden kann, ohne den Status selbst verwalten zu müssen. Allerdings sind Composeables mit internem Status in der Regel weniger wiederverwendbar und schwieriger zu testen.
Ein zustandsloser Composeable ist ein Composeable, das keinen Status hat. Eine einfache Möglichkeit, einen zustandslosen Dienst zu implementieren, ist das Staus-Hoisting.
Wenn Sie wiederverwendbare Composeables entwickeln, möchten Sie oft sowohl eine zustandsorientierte als auch eine zustandslose Version desselben Composeables freigeben. Die zustandsorientierte Version ist für Aufrufer praktisch, die sich nicht um den Status kümmern müssen. Die zustandslose Version ist für Aufrufer erforderlich, die den Status steuern oder anheben müssen.
Status-Hoisting
Das Zustands-Hoisting in Compose ist ein Muster, bei dem der Zustand an den Aufrufer eines Compose-Objekts verschoben wird, um es zustandslos zu machen. Beim allgemeinen Muster für das Heben des Status in Jetpack Compose wird die Statusvariable durch zwei Parameter ersetzt:
value: T
:Der aktuelle Wert, der angezeigt werden sollonValueChange: (T) -> Unit
:Ereignis, bei dem die Änderung des Werts angefordert wird.T
ist der vorgeschlagene neue Wert.
Sie sind jedoch nicht auf onValueChange
beschränkt. Wenn für die Composeable-Funktion spezifischere Ereignisse geeignet sind, sollten Sie sie mit Lambdas definieren.
Ein Zustand, der auf diese Weise gezogen wird, hat einige wichtige Eigenschaften:
- Single Source of Truth:Durch das Verschieben des Zustands, anstatt ihn zu duplizieren, wird dafür gesorgt, dass es nur eine einzige Informationsquelle gibt. So lassen sich Fehler vermeiden.
- Eingekapselt:Nur zustandsorientierte Composeables können ihren Status ändern. Sie ist komplett intern.
- Teilbar:Der gehostete Status kann für mehrere Composeables freigegeben werden. Wenn Sie
name
in einem anderen Composeable lesen möchten, ist das mithilfe von Hoisting möglich. - Interceptable:Aufrufer der zustandslosen zusammensetzbaren Funktionen können Ereignisse vor dem Ändern des Status ignorieren oder ändern.
- Entkoppelt:Der Status der zustandslosen Composeables kann überall gespeichert werden. Beispielsweise ist es jetzt möglich,
name
in eineViewModel
zu verschieben.
Im Beispiel extrahieren Sie name
und onValueChange
aus HelloContent
und verschieben sie im Baum nach oben zu einem HelloScreen
-Komposit, das 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 der Status aus HelloContent
ausgelagert wird, lässt sich die Komponente leichter analysieren, in verschiedenen Situationen wiederverwenden und testen. HelloContent
wird von der Speicherung seines Status entkoppelt. Wenn Sie HelloScreen
ändern oder ersetzen, müssen Sie die Implementierung von HelloContent
nicht ändern.

Das Muster, bei dem der Status sinkt und die Ereignisse steigen, wird als einseitiger Datenfluss bezeichnet. In diesem Fall sinkt der Status von HelloScreen
auf HelloContent
und die Ereignisse steigen von HelloContent
auf HelloScreen
an. Wenn Sie einem unidirektionalen Datenfluss folgen, können Sie Komponenten, die den Status in der Benutzeroberfläche anzeigen, von den Teilen Ihrer App entkoppeln, die den Status speichern und ändern.
Weitere Informationen finden Sie auf der Seite Windenstatus.
Status in Compose wird wiederhergestellt
Die rememberSaveable
API verhält sich ähnlich wie remember
, da der Status bei Neuzusammensetzungen und auch bei der Neuerstellung von Aktivitäten oder Prozessen mithilfe des Mechanismus für den gespeicherten Instanzstatus beibehalten wird. Das passiert beispielsweise, wenn das Display gedreht wird.
Möglichkeiten zum Speichern des Zustands
Alle Datentypen, die der Bundle
hinzugefügt werden, werden automatisch gespeichert. Wenn Sie etwas speichern möchten, das nicht zur Bundle
hinzugefügt werden kann, haben Sie mehrere Möglichkeiten.
Parcelize
Die einfachste Lösung besteht darin, dem Objekt die Annotation @Parcelize
hinzuzufügen. Das Objekt kann dann in Pakete aufgeteilt und in Sets angeboten werden. Mit diesem Code wird beispielsweise ein teilbarer City
-Datentyp 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 für die Umwandlung eines Objekts in eine Reihe von Werten definieren, die das System in der 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
Wenn Sie die Schlüssel für die Zuordnung nicht definieren möchten, können Sie auch listSaver
verwenden und die Indexe als Schlüssel verwenden:
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")) } }
Staatsinhaber in Compose
Das einfache Zustands-Hoisting kann in den zusammensetzbaren Funktionen selbst verwaltet werden. Wenn jedoch die Menge an Status, die Sie im Blick behalten müssen, zunimmt oder die Logik in kompositionsfähigen Funktionen ausgeführt werden muss, sollten Sie die Verantwortung für Logik und Status an andere Klassen delegieren: Statushalter.
Weitere Informationen finden Sie in der Dokumentation zum Zustandsaufstieg in Compose oder allgemeiner in der Seite State Holder und UI-Status im Architekturleitfaden.
Berechnungen für „Denken Sie daran“ bei Schlüsseländerungen noch einmal auslösen
Die remember
API wird häufig zusammen mit MutableState
verwendet:
var name by remember { mutableStateOf("") }
Hier sorgt die remember
-Funktion dafür, dass der MutableState
-Wert Neuzusammensetzungen überdauert.
Im Allgemeinen nimmt remember
einen calculation
-Lambda-Parameter an. Bei der ersten Ausführung von remember
wird das Lambda calculation
aufgerufen und das Ergebnis gespeichert. Bei der Neuzusammensetzung gibt remember
den zuletzt gespeicherten Wert zurück.
Neben dem Caching des Status können Sie mit remember
auch Objekte oder Ergebnisse eines Vorgangs in der Komposition speichern, deren Initialisierung oder Berechnung aufwendig ist. Sie müssen diese Berechnung nicht bei jeder Neuzusammensetzung wiederholen.
Ein Beispiel ist das Erstellen dieses ShaderBrush
-Objekts, was ein teurer Vorgang ist:
val brush = remember { ShaderBrush( BitmapShader( ImageBitmap.imageResource(res, avatarRes).asAndroidBitmap(), Shader.TileMode.REPEAT, Shader.TileMode.REPEAT ) ) }
remember
speichert den Wert, bis er die Komposition verlässt. Es gibt jedoch eine Möglichkeit, den im Cache gespeicherten Wert zu entwerten. Die remember
API akzeptiert auch den Parameter key
oder keys
. Wenn sich einer dieser Schlüssel ändert, remember
erhebt den Cache auf und führt den Lambda-Block noch einmal aus, wenn die Funktion das nächste Mal neu erstellt wird. Mit diesem Mechanismus können Sie die Lebensdauer eines Objekts in der Komposition steuern. Die Berechnung bleibt gültig, bis sich die Eingaben ändern, anstatt 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 Hintergrundpaint für eine zusammensetzbare Box
-Funktion verwendet. remember
speichert die Instanz ShaderBrush
, da die Neuerstellung, wie oben erläutert, kostspielig ist. Für remember
wird avatarRes
als key1
-Parameter verwendet, also das ausgewählte Hintergrundbild. Wenn sich avatarRes
ändert, wird der Pinsel mit dem neuen Bild neu zusammengesetzt und auf die 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 Statushalterklasse
MyAppState
verschoben. Sie stellt eine rememberMyAppState
-Funktion bereit, um eine Instanz der Klasse mit remember
zu initialisieren. Ein gängiges Muster in Compose ist es, solche Funktionen zum Erstellen einer Instanz verfügbar zu machen, die Neuzusammensetzungen übersteht. Die Funktion rememberMyAppState
empfängt windowSizeClass
, das als key
-Parameter für remember
dient. Wenn sich dieser Parameter ändert, muss die Anwendung die einfache State Holder-Klasse mit dem neuesten Wert neu erstellen. Das kann z. B. 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 den gespeicherten Wert ungültig zu machen.
Status mit Schlüsseln über die Neuzusammensetzung hinaus speichern
Die rememberSaveable
API ist ein Wrapper für remember
, mit dem Daten in einer Bundle
gespeichert werden können. Mit dieser API bleibt der Status nicht nur nach der Neuzusammensetzung, sondern auch nach der Wiederherstellung von Aktivitäten und dem systeminitiierten Beenden des Prozesses erhalten.
rememberSaveable
empfängt input
-Parameter zu demselben Zweck wie remember
keys
. Der Cache wird ungültig, wenn sich eine der Eingaben ändert. Wenn die Funktion das nächste Mal neu erstellt wird, führt rememberSaveable
den Berechnungs-Lambda-Block noch einmal aus.
Im folgenden Beispiel speichert rememberSaveable
userTypedQuery
, 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 Status und Jetpack Compose finden Sie in den folgenden zusätzlichen Ressourcen.
Produktproben
Codelabs
Videos
Blogs
Empfehlungen für dich
- Hinweis: Der Linktext wird angezeigt, wenn JavaScript deaktiviert ist.
- Benutzeroberfläche für das Schreiben erstellen
- UI-Status in „Schreiben“ speichern
- Nebeneffekte in Compose