UI-Zustände speichern

In diesem Leitfaden werden die Erwartungen der Nutzer bezüglich des UI-Status und die verfügbaren Optionen erläutert. zur Beibehaltung des Zustands.

Das Speichern und Wiederherstellen des UI-Status einer Aktivität nach einer Aktivitäten oder Anwendungen werden essenziell für einen guten Benutzer Nutzererfahrung. Nutzer erwarten, dass der Status der Benutzeroberfläche gleich bleibt, aber das System kann und zerstören Sie die Aktivität und ihren gespeicherten Status.

Um die Lücke zwischen Nutzererwartung und Systemverhalten zu schließen, Kombination der folgenden Methoden:

Die optimale Lösung hängt von der Komplexität Ihrer UI-Daten, der Verwendung und ein Gleichgewicht zwischen Geschwindigkeit des Datenzugriffs und Speichernutzung finden.

Sicherstellen, dass die App den Anforderungen der Nutzer entspricht Erwartungen und bietet eine schnelle, . Vermeiden Sie Verzögerungen beim Laden von Daten in die Benutzeroberfläche, insbesondere nach häufigen Konfigurationsänderungen wie die Rotation.

Nutzererwartungen und Systemverhalten

Abhängig von der Aktion, die Nutzende durchführen, erwarten sie entweder, dass der Aktivitätsstatus gelöscht oder der Zustand beibehalten werden soll. In einigen Fällen kann das System was die Nutzenden erwarten. In anderen Fällen benötigt das System was die Nutzenden erwarten.

Vom Nutzer initiierte Ablehnung des UI-Status

Der Nutzer erwartet, dass beim Starten einer Aktivität der vorübergehende UI-Status Diese Aktivität bleibt gleich, bis der Nutzer sie vollständig schließt. So kann der Nutzer eine Aktivität vollständig schließen:

  • Wischen Sie die Aktivität aus dem Bildschirm „Übersicht (Letzte)“.
  • Beenden der App über den Bildschirm „Einstellungen“ oder erzwungenes Beenden der App
  • Das Gerät wird neu gestartet.
  • Eine Art „Fertigstellen“ Aktion (die durch Activity.finish().

Die Nutzenden gehen in diesen vollständigen Ablehnungsfällen davon aus, dass sie die Aktivität dauerhaft verlassen haben, erwarten sie, dass die Aktivität in einem sauberen Zustand beginnt. Das zugrunde liegende System dass das Verhalten der Nutzer den Erwartungen entspricht: wird die Activity-Instanz gelöscht und zusammen mit und jeden gespeicherten Instanzstatuseintrag, der mit dem Aktivitäten.

Es gibt einige Ausnahmen von dieser Regel bezüglich vollständiger Ablehnungen, z. B. eine erwarten Nutzer, dass sie von einem Browser genau auf die Webseite gelangen, die sie gesucht haben. bevor sie den Browser über die Schaltfläche „Zurück“ verlassen haben.

Vom System initiierte Ablehnung des UI-Status

Ein Nutzer erwartet, dass der UI-Status einer Aktivität während einer Konfigurationsänderung, z. B. Drehung oder Wechsel in den Mehrfenstermodus. Standardmäßig zerstört das System die Aktivität, wenn eine solche Konfiguration Änderung auftritt, wodurch alle in der Activity-Instanz gespeicherten UI-Status gelöscht werden. Bis Weitere Informationen zu Gerätekonfigurationen finden Sie in der Konfigurationsreferenzseite. Hinweis: Dies ist möglich, wird aber nicht empfohlen. um das Standardverhalten für Konfigurationsänderungen zu überschreiben. Weitere Informationen finden Sie unter Handhabung der selbst konfigurieren.

Nutzer erwarten außerdem, dass der UI-Status Ihrer Aktivität gleich bleibt, wenn sie wechseln Sie vorübergehend zu einer anderen App und kehren Sie später zu Ihrer App zurück. Für Beispiel: Der Nutzer führt eine Suche in Ihrer Suchaktivität durch und drückt dann die Startbildschirmtaste oder einen Anruf entgegennimmt, wenn er zur Suchaktivität zurückkehrt erwarten sie, dass das Keyword und die Ergebnisse für die Suche weiterhin vorhanden sind, genau wie vorher.

In diesem Szenario wird die App im Hintergrund platziert und das System kann sein Bestes. damit der App-Prozess im Arbeitsspeicher Es kann jedoch sein, dass das System während der Nutzer nicht mit anderen Apps interagiert. In solchen Fällen in einem Fall wird die Activity-Instanz mit allen darin gespeicherten Statusdaten gelöscht. Wenn der Nutzer die App neu startet, ist die Aktivität unerwartet bereinigt. Weitere Informationen zum Beenden von Prozessen finden Sie unter Prozesse und Anwendungslebenszyklus.

Optionen zum Beibehalten des UI-Status

Wenn die Erwartungen der Nutzenden an den Status der Benutzeroberfläche nicht mit dem Standardsystem übereinstimmen muss der Status der Benutzeroberfläche des Nutzers gespeichert und wiederhergestellt werden. vom System initiierte Löschung ist für den Nutzer transparent.

Die Optionen zum Beibehalten des UI-Status variieren je nach den folgenden Dimensionen: die sich auf die User Experience auswirken:

ViewModel Status der gespeicherten Instanz Nichtflüchtiger Speicher
Speicherort im Arbeitsspeicher im Arbeitsspeicher auf Datenträger oder im Netzwerk
Überlebt Konfigurationsänderung Ja Ja Ja
Überlebt den Tod eines vom System initiierten Prozesses Nein Ja Ja
Überlebt das Schließen der Nutzeraktivität/onFinish() Nein Nein Ja
Dateneinschränkungen komplexe Objekte sind in Ordnung, aber der Platz ist durch den verfügbaren Arbeitsspeicher begrenzt. Nur für primitive Typen und einfache, kleine Objekte wie String nur begrenzt durch Speicherplatz oder Kosten / Zeit des Abrufs von der Netzwerkressource
Lese-/Schreibzeit schnell (nur Arbeitsspeicherzugriff) langsam (erfordert Serialisierung/Deserialisierung) langsam (erfordert Festplattenzugriff oder Netzwerktransaktion)

Konfigurationsänderungen mit ViewModel verarbeiten

ViewModel eignet sich ideal zum Speichern und Verwalten von UI-Daten, während der Nutzer die Anwendung aktiv nutzen. Sie ermöglicht schnellen Zugriff auf Daten der Benutzeroberfläche und hilft Ihnen, den erneuten Abruf von Daten aus dem Netzwerk oder der Festplatte durch Rotation, Fenstergröße und andere häufig auftretende Konfigurationsänderungen. Um zu erfahren, wie Sie ein ViewModel finden Sie im ViewModel-Leitfaden.

ViewModel behält die Daten im Speicher bei, sodass es kostengünstiger ist, sie abzurufen als Daten von der Festplatte oder dem Netzwerk abzurufen. Ein ViewModel ist mit einer Aktivität verknüpft (oder ein anderer Lebenszyklusinhaber) – er bleibt während einer Konfiguration im Arbeitsspeicher geändert wird und das ViewModel automatisch mit dem neuen Aktivitätsinstanz, die sich aus der Konfigurationsänderung ergibt.

ViewModels werden automatisch vom System gelöscht, wenn der Nutzer Ihrer Aktivität bzw. Ihrem Fragment oder wenn Sie finish() aufrufen, was bedeutet, dass der Status wie vom Nutzer erwartet gelöscht.

Im Gegensatz zum gespeicherten Instanzstatus werden ViewModels während einer vom System initiierten Prozessbeendigung. Um Daten neu zu laden, nachdem ein vom System initiierter Prozess in einem ViewModel verwenden Sie die SavedStateHandle API. Wenn die Daten dagegen die mit der Benutzeroberfläche zusammenhängen und nicht im ViewModel enthalten sein müssen, verwenden Sie onSaveInstanceState() im Ansichtssystem oder rememberSaveable in Jetpack Schreiben. Wenn es sich bei den Daten um Anwendungsdaten handelt, ist es möglicherweise besser, es auf der Festplatte gespeichert.

Wenn Sie bereits eine In-Memory-Lösung zum Speichern des UI-Status haben müssen Sie ViewModel möglicherweise nicht verwenden.

Gespeicherten Instanzstatus als Sicherung verwenden, um den vom System initiierten Prozess zu beenden

Den onSaveInstanceState()-Callback im Ansichtssystem rememberSaveable in Jetpack Compose und SavedStateHandle in Jetpack Compose ViewModels speichert Daten, die zum erneuten Laden des Status eines UI-Controllers erforderlich sind, z. B. Aktivität oder ein Fragment, wenn das System dies zerstört und später Controller. So implementieren Sie den gespeicherten Instanzstatus mithilfe von onSaveInstanceState, siehe Aktivitätsstatus speichern und wiederherstellen in der Leitfaden zum Aktivitätslebenszyklus

Gespeicherte Instanzstatus-Bundles bleiben sowohl bei Konfigurationsänderungen als auch sind jedoch durch Speicher und Geschwindigkeit begrenzt, da die verschiedenen APIs Daten serialisieren. Die Serialisierung kann viel Arbeitsspeicher verbrauchen, wenn die Objekte, serialisiert werden, kompliziert. Weil dieser Vorgang im Hauptthread erfolgt während einer Konfigurationsänderung führt, kann eine Serialisierung mit langer Ausführungszeit dazu führen, und das visuelle Rauschen.

Verwenden Sie den gespeicherten Instanzstatus nicht zum Speichern großer Datenmengen, z. B. Bitmaps, noch komplexe Datenstrukturen, die eine lange Serialisierung erfordern, Deserialisierung. Speichern Sie stattdessen nur primitive Typen und einfache, kleine Objekte. wie String. Verwenden Sie daher den gespeicherten Instanzstatus, um eine minimale Menge an Daten, die erforderlich sind, z. B. eine ID, um die Daten neu zu erstellen, die zur Wiederherstellung der Benutzeroberfläche erforderlich sind für den Fall, dass die anderen Persistenzmechanismen fehlschlagen, Meiste Apps sollten dies implementieren, um den vom System initiierten Prozess zu beheben.

Je nach Anwendungsfall Ihrer Anwendung müssen Sie gespeicherte Instanzen möglicherweise nicht verwenden überhaupt nicht. Beispielsweise kann ein Browser die Nutzenden zurück zu der Webseite, die sie vor dem Verlassen des Browsers aufgerufen haben. Wenn Ihre Aktivitäten sich so verhält, können Sie auf den gespeicherten Instanzstatus verzichten und stattdessen alles lokal abrufen.

Wenn Sie eine Aktivität von einem Intent aus öffnen, wird das Bündel von Extras an die Aktivität geliefert wird, wenn sich die Konfiguration ändert, die Aktivität wiederherstellt. Wenn ein UI-Statusdatenelement, z. B. eine Suche, als zusätzliche Intent übergeben wurden, als die Aktivität gestartet wurde, kann das Extras-Bundle anstelle des gespeicherten Instanzstatus-Bundles verwenden. Weitere Informationen Weitere Informationen zu Intent-Extras finden Sie unter Intent- und Intent-Filter.

In beiden Fällen sollten Sie ein ViewModel verwenden, um zum erneuten Laden von Daten aus der Datenbank während einer Konfigurationsänderung.

In Fällen, in denen die UI-Daten einfach und kompakt sind, können Sie mit gespeicherten Instanzstatus-APIs die Statusdaten erhalten.

Mit SavedStateRegistry in den gespeicherten Status einhängen

Ab Fragment 1.1.0 oder dessen transitive Abhängigkeit Activity 1.0.0, UI-Controller wie Activity oder Fragment, implementieren SavedStateRegistryOwner und geben Sie SavedStateRegistry an, die an diesen Verantwortlichen gebunden sind. Mit SavedStateRegistry können Komponenten verbunden werden in des UI-Controllers gespeichert, um ihn zu nutzen oder beizutragen. Beispiel: Das Modul „Saved State“ für ViewModel erstellt mit SavedStateRegistry eine SavedStateHandle und stellen Sie sie Ihren ViewModel-Objekten bereit. Sie können die Daten abrufen, den SavedStateRegistry in Ihrem UI-Controller durch Aufrufen von getSavedStateRegistry()

Komponenten, die zum gespeicherten Status beitragen, müssen SavedStateRegistry.SavedStateProvider, mit dem eine einzelne Methode definiert wird mit dem Namen saveState(). Mit der Methode saveState() kann Ihre Komponente gibt ein Bundle zurück, das alle Statuswerte enthält, die von dieser Komponente gespeichert werden sollen. SavedStateRegistry ruft diese Methode während der Speicherphase der Benutzeroberfläche auf des Lebenszyklus des Controllers.

Kotlin

class SearchManager : SavedStateRegistry.SavedStateProvider {
    companion object {
        private const val QUERY = "query"
    }

    private val query: String? = null

    ...

    override fun saveState(): Bundle {
        return bundleOf(QUERY to query)
    }
}

Java

class SearchManager implements SavedStateRegistry.SavedStateProvider {
    private static String QUERY = "query";
    private String query = null;
    ...

    @NonNull
    @Override
    public Bundle saveState() {
        Bundle bundle = new Bundle();
        bundle.putString(QUERY, query);
        return bundle;
    }
}

Um eine SavedStateProvider zu registrieren, rufen Sie registerSavedStateProvider() auf unter SavedStateRegistry und übergeben einen Schlüssel, der mit den Daten des Anbieters verknüpft werden soll als und dem Anbieter. Die zuvor für den Anbieter gespeicherten Daten können durch Aufrufen von consumeRestoredStateForKey() aus dem gespeicherten Status abgerufen in der SavedStateRegistry, wobei der Schlüssel übergeben wird, der mit der Methode Daten.

Innerhalb von Activity oder Fragment können Sie SavedStateProvider in onCreate() nach dem Anruf bei super.onCreate(). Alternativ können Sie LifecycleObserver auf einem SavedStateRegistryOwner, bei dem Folgendes implementiert wird: LifecycleOwner und registrieren Sie die SavedStateProvider, sobald die Es findet ON_CREATE statt. Mit einem LifecycleObserver können Sie das die Registrierung und der Abruf des zuvor gespeicherten Status aus dem SavedStateRegistryOwner selbst.

Kotlin

class SearchManager(registryOwner: SavedStateRegistryOwner) : SavedStateRegistry.SavedStateProvider {
    companion object {
        private const val PROVIDER = "search_manager"
        private const val QUERY = "query"
    }

    private val query: String? = null

    init {
        // Register a LifecycleObserver for when the Lifecycle hits ON_CREATE
        registryOwner.lifecycle.addObserver(LifecycleEventObserver { _, event ->
            if (event == Lifecycle.Event.ON_CREATE) {
                val registry = registryOwner.savedStateRegistry

                // Register this object for future calls to saveState()
                registry.registerSavedStateProvider(PROVIDER, this)

                // Get the previously saved state and restore it
                val state = registry.consumeRestoredStateForKey(PROVIDER)

                // Apply the previously saved state
                query = state?.getString(QUERY)
            }
        }
    }

    override fun saveState(): Bundle {
        return bundleOf(QUERY to query)
    }

    ...
}

class SearchFragment : Fragment() {
    private var searchManager = SearchManager(this)
    ...
}

Java

class SearchManager implements SavedStateRegistry.SavedStateProvider {
    private static String PROVIDER = "search_manager";
    private static String QUERY = "query";
    private String query = null;

    public SearchManager(SavedStateRegistryOwner registryOwner) {
        registryOwner.getLifecycle().addObserver((LifecycleEventObserver) (source, event) -> {
            if (event == Lifecycle.Event.ON_CREATE) {
                SavedStateRegistry registry = registryOwner.getSavedStateRegistry();

                // Register this object for future calls to saveState()
                registry.registerSavedStateProvider(PROVIDER, this);

                // Get the previously saved state and restore it
                Bundle state = registry.consumeRestoredStateForKey(PROVIDER);

                // Apply the previously saved state
                if (state != null) {
                    query = state.getString(QUERY);
                }
            }
        });
    }

    @NonNull
    @Override
    public Bundle saveState() {
        Bundle bundle = new Bundle();
        bundle.putString(QUERY, query);
        return bundle;
    }

    ...
}

class SearchFragment extends Fragment {
    private SearchManager searchManager = new SearchManager(this);
    ...
}

Lokale Persistenz verwenden, um den Prozessabschluss bei komplexen oder großen Daten zu bewältigen

Nichtflüchtiger lokaler Speicher, z. B. eine Datenbank oder gemeinsame Einstellungen, bleibt erhalten. solange Ihre Anwendung auf dem Gerät des Nutzers installiert ist (es sei denn, Nutzer die Daten für Ihre App löscht). Ein solcher lokaler Speicher bleibt die vom System initiierte Aktivität und der Anwendungsprozess beendet werden, kann es teuer sein, abgerufen, da er aus dem lokalen Speicher im Arbeitsspeicher gelesen werden muss. Häufig Dieser nichtflüchtige lokale Speicher ist möglicherweise bereits Teil Ihrer Anwendung Architektur alle Daten speichern, die nicht verloren gehen sollen, wenn Sie die Aktivitäten.

Weder ViewModel noch der gespeicherte Instanzstatus sind langfristige Speicherlösungen. sind daher kein Ersatz für einen lokalen Speicher wie eine Datenbank. Stattdessen sollten diese Mechanismen nur zur temporären Speicherung des UI-Status vorübergehend verwenden und Nichtflüchtigen Speicher für andere App-Daten verwenden Siehe Leitfaden zur Anwendungsarchitektur für weitere Informationen dazu, wie du lokalen Speicher nutzen kannst, um dein App-Modell beizubehalten (z.B. bei einem Neustart des Geräts).

UI-Status verwalten: Teilung und Eroberung

Sie können den Status der Benutzeroberfläche effizient speichern und wiederherstellen, indem Sie die Arbeit zwischen den verschiedene Arten von Persistenzmechanismen. In den meisten Fällen wird jeder dieser Mechanismen Daten, die in der Aktivität verwendet werden, basierend auf dem Kompromisse bei Datenkomplexität, Zugriffsgeschwindigkeit und Lebensdauer:

  • Lokale Persistenz: Speichert alle Anwendungsdaten, die bei folgenden Ereignissen nicht verloren gehen sollen. die Aktivität öffnen und schließen.
    • Beispiel: Eine Sammlung von Songobjekten, die Audiodateien enthalten können und Metadaten.
  • ViewModel: Speichert alle Daten im Arbeitsspeicher, die zum Anzeigen des der zugehörigen UI, den Status der Bildschirm-UI.
    • Beispiel: Die Titelobjekte bei der letzten Suche und der letzten Suche Suchanfrage.
  • Gespeicherter Instanzstatus: Speichert eine kleine Datenmenge, die für das Neuladen erforderlich ist UI-Status, wenn das System anhält und dann die Benutzeroberfläche neu erstellt Anstelle von um komplexe Objekte zu speichern, werden die komplexen Objekte im lokalen Speicher Eine eindeutige ID für diese Objekte in den gespeicherten Instanzstatus-APIs.
    • Beispiel: Die letzte Suchanfrage wird gespeichert.

Stellen Sie sich als Beispiel eine Aktivität vor, bei der Sie der Songs aus unserer Mediathek. So sollten verschiedene Ereignisse gehandhabt werden:

Wenn der Nutzer einen Song hinzufügt, delegiert ViewModel sofort das diese Daten lokal bereitstellen. Soll der neu hinzugefügte Song auf der Benutzeroberfläche angezeigt werden, sollten auch die Daten im ViewModel-Objekt aktualisieren, um die Hinzufügung von des Songs. Denken Sie daran, alle Datenbankeinfügungen aus dem Hauptthread heraus vorzunehmen.

Wenn der Nutzer nach einem Song sucht, sind die komplexen Titeldaten, die Sie aus dem sollte sie sofort im ViewModel-Objekt als Teil der Bildschirm-UI-Status angezeigt.

Wenn die Aktivität in den Hintergrund verschoben wird und das System die gespeicherten Instanzstatus-APIs muss die Suchanfrage im Status der gespeicherten Instanz gespeichert werden, falls der Prozess neu erstellt wird. Da die Informationen zum Laden der App-Daten beibehalten, Suchanfrage im ViewModel speichern SavedStateHandle Das sind alle Informationen, die Sie zum Laden der Daten und die Benutzeroberfläche in ihren aktuellen Zustand zurückversetzen.

Komplexe Zustände wiederherstellen: Teile zusammensetzen

Wenn es Zeit ist, dass Nutzende zur Aktivität zurückkehren, gibt es zwei mögliche Szenarien für die Neuerstellung der Aktivität:

  • Die Aktivität wird neu erstellt, nachdem sie vom System angehalten wurde. Die die Abfrage in einem gespeicherten Instanzstatus-Bundle gespeichert ist, und die UI sollte die Abfrage an ViewModel übergeben, wenn SavedStateHandle nicht verwendet wird. ViewModel erkennt, dass keine Suchergebnisse im Cache gespeichert wurden, und delegiert Laden der Suchergebnisse anhand der gegebenen Suchanfrage.
  • Die Aktivität wird nach einer Konfigurationsänderung erstellt. Seit dem ViewModel Instanz nicht gelöscht wurde, sind im ViewModel alle Informationen im Cache gespeichert. und muss die Datenbank nicht noch einmal abfragen.

Weitere Informationen

Weitere Informationen zum Speichern von UI-Statuswerten finden Sie in den folgenden Ressourcen.

Blogs