Je nachdem, wohin der Status verschoben wird, und je nach erforderlicher Logik können Sie verschiedene APIs zum Speichern und Wiederherstellen des UI-Status verwenden. Jede App verwendet eine Kombination aus APIs, um dies bestmöglich zu erreichen.
Jede Android-App kann ihren UI-Status aufgrund der Neuerstellung von Aktivitäten oder Prozessen verlieren. Dieser Statusverlust kann aufgrund der folgenden Ereignisse auftreten:
- Konfigurationsänderungen Die Aktivität wird zerstört und neu erstellt, es sei denn, die Konfigurationsänderung wird manuell verarbeitet.
- Vom System initiierter Prozessabbruch: Die App befindet sich im Hintergrund und das Gerät gibt Ressourcen wie Arbeitsspeicher frei, die von anderen Prozessen verwendet werden können.
Das Beibehalten des Status nach diesen Ereignissen ist für eine positive Nutzererfahrung unerlässlich. Welcher Status beibehalten werden soll, hängt von den individuellen Nutzerflows Ihrer App ab. Als Best Practice sollten Sie mindestens die Nutzereingabe und den navigationsbezogenen Status beibehalten. Beispiele hierfür sind die Scrollposition einer Liste, die ID des Elements, zu dem der Nutzer weitere Informationen wünscht, die laufende Auswahl von Nutzereinstellungen oder Eingaben in Textfeldern.
Auf dieser Seite werden die APIs zusammengefasst, die zum Speichern des UI-Zustands verfügbar sind, je nachdem, wohin der Zustand verschoben wird und welche Logik ihn benötigt.
UI-Logik
Wenn Ihr Status in der Benutzeroberfläche übertragen wird, entweder in komponierbaren Funktionen oder in einfachen Status-Holder-Klassen, die auf die Komposition beschränkt sind, können Sie rememberSaveable verwenden, um den Status bei der Neuerstellung von Aktivitäten und Prozessen beizubehalten.
Im folgenden Snippet wird rememberSaveable verwendet, um den Status eines einzelnen booleschen UI-Elements zu speichern:
@Composable fun ChatBubble( message: Message ) { var showDetails by rememberSaveable { mutableStateOf(false) } ClickableText( text = AnnotatedString(message.content), onClick = { showDetails = !showDetails } ) if (showDetails) { Text(message.timestamp) } }
showDetails ist eine boolesche Variable, die speichert, ob die Chatblase minimiert oder maximiert ist.
rememberSaveable speichert den Status des UI-Elements in einem Bundle über den Mechanismus für den gespeicherten Instanzstatus.
Sie kann primitive Typen automatisch im Bundle speichern. Wenn der Status in einem Typ gespeichert wird, der nicht primitiv ist, z. B. in einer Datenklasse, können Sie verschiedene Speichermechanismen verwenden, z. B. die Annotation Parcelize, Compose-APIs wie listSaver und mapSaver oder eine benutzerdefinierte Saver-Klasse, die die Compose-Laufzeitklasse Saver erweitert. Weitere Informationen zu diesen Methoden finden Sie in der Dokumentation unter Möglichkeiten zum Speichern des Status.
Im folgenden Snippet speichert die rememberLazyListState Compose API LazyListState, die aus dem Scrollstatus eines LazyColumn oder LazyRow besteht, mit rememberSaveable. Dazu wird ein LazyListState.Saver verwendet, ein benutzerdefinierter Saver, der den Scrollstatus speichern und wiederherstellen kann. Nach dem erneuten Erstellen einer Aktivität oder eines Prozesses (z. B. nach einer Konfigurationsänderung wie dem Ändern der Geräteausrichtung) wird der Scrollstatus beibehalten.
@Composable fun rememberLazyListState( initialFirstVisibleItemIndex: Int = 0, initialFirstVisibleItemScrollOffset: Int = 0 ): LazyListState { return rememberSaveable(saver = LazyListState.Saver) { LazyListState( initialFirstVisibleItemIndex, initialFirstVisibleItemScrollOffset ) } }
Best Practice
rememberSaveable verwendet ein Bundle zum Speichern des UI-Zustands, das von anderen APIs gemeinsam genutzt wird, die ebenfalls darauf schreiben, z. B. onSaveInstanceState()-Aufrufe in Ihrer Aktivität. Die Größe dieses Bundle ist jedoch begrenzt. Das Speichern großer Objekte kann zu TransactionTooLarge-Ausnahmen zur Laufzeit führen. Das kann besonders bei einzelnen Activity-Apps problematisch sein, in denen derselbe Bundle in der gesamten App verwendet wird.
Um diese Art von Absturz zu vermeiden, sollten Sie keine großen komplexen Objekte oder Listen von Objekten im Bundle speichern.
Speichern Sie stattdessen den erforderlichen Mindeststatus, z. B. IDs oder Schlüssel, und verwenden Sie diese, um das Wiederherstellen komplexerer UI-Zustände an andere Mechanismen wie persistenten Speicher zu delegieren.
Diese Designentscheidungen hängen von den spezifischen Anwendungsfällen für Ihre App und davon ab, wie Ihre Nutzer sie verwenden möchten.
Statuswiederherstellung prüfen
Sie können überprüfen, ob der mit rememberSaveable in Ihren Compose-Elementen gespeicherte Status korrekt wiederhergestellt wird, wenn die Aktivität oder der Prozess neu erstellt wird. Dafür gibt es bestimmte APIs, z. B. StateRestorationTester. Weitere Informationen finden Sie in der Dokumentation zum Testen.
Geschäftslogik
Wenn der UI-Elementstatus in ViewModel verschoben wird, weil er für die Geschäftslogik erforderlich ist, können Sie die APIs von ViewModel verwenden.
Einer der Hauptvorteile der Verwendung eines ViewModel in Ihrer Android-App ist, dass Konfigurationsänderungen automatisch verarbeitet werden. Wenn sich die Konfiguration ändert und die Aktivität zerstört und neu erstellt wird, bleibt der in ViewModel übergebene UI-Status im Arbeitsspeicher erhalten. Nach der Neuerstellung wird die alte ViewModel-Instanz an die neue Aktivitätsinstanz angehängt.
Eine ViewModel-Instanz übersteht jedoch keinen vom System initiierten Prozessabbruch. Wenn der UI-Zustand erhalten bleiben soll, verwenden Sie das Saved State module for ViewModel, das die SavedStateHandle API enthält.
Best Practice
SavedStateHandle verwendet auch den Bundle-Mechanismus zum Speichern des UI-Zustands. Sie sollten ihn daher nur zum Speichern des einfachen Zustands von UI-Elementen verwenden.
Der UI-Status des Bildschirms, der durch Anwenden von Geschäftsregeln und Zugreifen auf andere Ebenen Ihrer Anwendung als die UI erzeugt wird, sollte aufgrund seiner potenziellen Komplexität und Größe nicht in SavedStateHandle gespeichert werden. Sie können verschiedene Mechanismen zum Speichern komplexer oder großer Daten verwenden, z. B. lokalen nichtflüchtigen Speicher. Nach der Neuerstellung eines Prozesses wird der Bildschirm mit dem wiederhergestellten transienten Zustand neu erstellt, der in SavedStateHandle gespeichert wurde (falls vorhanden). Der UI-Zustand des Bildschirms wird dann noch einmal aus der Datenschicht generiert.
APIs von SavedStateHandle
SavedStateHandle bietet verschiedene APIs zum Speichern des Status von UI-Elementen, insbesondere:
Schreiben State |
saveable() |
|---|---|
StateFlow |
getStateFlow() |
State verfassen
Verwenden Sie die saveable API von SavedStateHandle, um den Status von UI-Elementen als MutableState zu lesen und zu schreiben. So bleibt er auch nach dem Neuerstellen von Aktivitäten und Prozessen erhalten und es ist nur minimaler Code erforderlich.
Die saveable API unterstützt sofort primitive Typen und empfängt einen stateSaver-Parameter für die Verwendung benutzerdefinierter Saver, genau wie rememberSaveable().
Im folgenden Snippet wird die Nutzereingabe, die in ein TextField eingegeben wurde, in message gespeichert:
class ConversationViewModel( savedStateHandle: SavedStateHandle ) : ViewModel() { var message by savedStateHandle.saveable(stateSaver = TextFieldValue.Saver) { mutableStateOf(TextFieldValue("")) } private set fun update(newMessage: TextFieldValue) { message = newMessage } /*...*/ } val viewModel = ConversationViewModel(SavedStateHandle()) @Composable fun UserInput(/*...*/) { TextField( value = viewModel.message, onValueChange = { viewModel.update(it) } ) }
Weitere Informationen zur Verwendung der saveable API finden Sie in der SavedStateHandle-Dokumentation.
StateFlow
Verwenden Sie getStateFlow(), um den Status des UI-Elements zu speichern und als Flow aus SavedStateHandle zu verwenden. StateFlow ist schreibgeschützt und die API erfordert, dass Sie einen Schlüssel angeben, damit Sie den Flow ersetzen und einen neuen Wert ausgeben können. Mit dem konfigurierten Schlüssel können Sie StateFlow abrufen und den neuesten Wert erfassen.
Im folgenden Snippet ist savedFilterType eine StateFlow-Variable, in der ein Filtertyp gespeichert wird, der auf eine Liste von Chat-Channels in einer Chat-App angewendet wird:
private const val CHANNEL_FILTER_SAVED_STATE_KEY = "ChannelFilterKey" class ChannelViewModel( channelsRepository: ChannelsRepository, private val savedStateHandle: SavedStateHandle ) : ViewModel() { private val savedFilterType: StateFlow<ChannelsFilterType> = savedStateHandle.getStateFlow( key = CHANNEL_FILTER_SAVED_STATE_KEY, initialValue = ChannelsFilterType.ALL_CHANNELS ) private val filteredChannels: Flow<List<Channel>> = combine(channelsRepository.getAll(), savedFilterType) { channels, type -> filter(channels, type) }.onStart { emit(emptyList()) } fun setFiltering(requestType: ChannelsFilterType) { savedStateHandle[CHANNEL_FILTER_SAVED_STATE_KEY] = requestType } /*...*/ } enum class ChannelsFilterType { ALL_CHANNELS, RECENT_CHANNELS, ARCHIVED_CHANNELS }
Jedes Mal, wenn der Nutzer einen neuen Filtertyp auswählt, wird setFiltering aufgerufen. Dadurch wird ein neuer Wert in SavedStateHandle gespeichert, der mit dem Schlüssel _CHANNEL_FILTER_SAVED_STATE_KEY_ verknüpft ist. savedFilterType ist ein Flow, der den letzten für den Schlüssel gespeicherten Wert ausgibt. filteredChannels hat den Flow abonniert, um die Kanalfilterung durchzuführen.
Weitere Informationen zur getStateFlow() API finden Sie in der Dokumentation zu SavedStateHandle.
Zusammenfassung
In der folgenden Tabelle sind die in diesem Abschnitt behandelten APIs zusammengefasst und es wird beschrieben, wann Sie die einzelnen APIs zum Speichern des UI-Zustands verwenden sollten:
| Ereignis | UI-Logik | Geschäftslogik in einem ViewModel |
|---|---|---|
| Konfigurationsänderungen | rememberSaveable |
Automatisch |
| Vom System initiierte Prozessbeendigung | rememberSaveable |
SavedStateHandle |
Welche API verwendet werden soll, hängt davon ab, wo der Status gespeichert wird und welche Logik dafür erforderlich ist. Verwenden Sie rememberSaveable für den Status, der in der UI-Logik verwendet wird. Wenn Sie Status verwenden, der in der Geschäftslogik verwendet wird, und ihn in einem ViewModel speichern, speichern Sie ihn mit SavedStateHandle.
Sie sollten die Bundle-APIs (rememberSaveable und SavedStateHandle) verwenden, um kleine Mengen an UI-Status zu speichern. Diese Daten sind zusammen mit anderen Speichermechanismen das Minimum, das erforderlich ist, um die Benutzeroberfläche in ihren vorherigen Zustand zurückzusetzen. Wenn Sie beispielsweise die ID eines Profils, das sich der Nutzer angesehen hat, im Bundle speichern, können Sie umfangreiche Daten wie Profildetails aus der Datenschicht abrufen.
Weitere Informationen zu den verschiedenen Möglichkeiten zum Speichern des UI-Zustands finden Sie in der allgemeinen Dokumentation zum Speichern des UI-Zustands und auf der Seite Datenebene im Architekturleitfaden.
Empfehlungen für Sie
- Hinweis: Linktext wird angezeigt, wenn JavaScript deaktiviert ist.
- Wo kann ich den Status ändern?
- Zustand und Jetpack Compose
- Listen und Raster