Navigationsstatus speichern und verwalten

In den folgenden Abschnitten werden Strategien zum Speichern des Backstacks und zum Speichern des mit Einträgen im Backstack verknüpften Status beschrieben.

Backstack speichern

Es ist wichtig, dass der Navigationsstatus Ihrer App bei verschiedenen Lebenszyklusereignissen beibehalten wird, einschließlich Konfigurationsänderungen und Prozessbeendigung, um eine gute Nutzererfahrung zu gewährleisten. In Navigation 3 verwalten Sie den Backstack selbst. Es gibt daher keine strengen Richtlinien dazu, wie Sie ihn erstellen oder speichern sollten. Navigation 3 bietet jedoch eine praktische Methode, mit der Sie einen speicherbaren Backstack erstellen können: rememberNavBackStack.

rememberNavBackStack verwenden

Die zusammensetzbare Funktion rememberNavBackStack wurde entwickelt, um einen Backstack zu erstellen, der Konfigurationsänderungen und das Beenden von Prozessen überdauert.

Damit rememberNavBackStack richtig funktioniert, muss jeder Schlüssel in Ihrem Backstack bestimmte Anforderungen erfüllen:

  • NavKey-Schnittstelle implementieren: Jeder Schlüssel im Backstack muss die NavKey-Schnittstelle implementieren. Dies dient als Markierungsschnittstelle, die der Bibliothek signalisiert, dass der Schlüssel gespeichert werden kann.
  • @Serializable-Annotation: Zusätzlich zur Implementierung von NavKey müssen Ihre Schlüsselklassen und -objekte mit der @Serializable-Annotation gekennzeichnet werden.

Das folgende Snippet zeigt eine korrekte Implementierung von rememberNavBackStack:

@Serializable
data object Home : NavKey

@Composable
fun NavBackStack() {
    val backStack = rememberNavBackStack(Home)
}

Einen Backstack mit Untertypen von NavKey erstellen

Die komponierbare Funktion rememberNavBackStack gibt ein NavBackStack<NavKey> zurück. Wenn in Ihrer App ein eigener Untertyp von NavKey definiert ist, von dem alle Schlüssel abgeleitet werden, können Sie diesen Typ beibehalten, indem Sie eine benutzerdefinierte remember-Funktion implementieren:

@Serializable
sealed interface MyAppNavKey : NavKey

@Serializable
data object ScreenA: MyAppNavKey

@Serializable
data class ScreenB(val id: String): MyAppNavKey

@Composable
fun rememberMyAppNavBackStack(vararg elements: MyAppNavKey): NavBackStack<MyAppNavKey> {
    return rememberSerializable(serializer = serializer()) {
        NavBackStack(*elements)
    }
}

@Composable
fun MyApp() {
    // defaultNavBackStack is NavBackStack<NavKey>
    val defaultNavBackStack = rememberNavBackStack(ScreenA)
    // myAppNavBackStack is NavBackStack<MyAppNavKey>
    val myAppNavBackStack = rememberMyAppNavBackStack(ScreenA)
}

Weitere Beispiele, auch zum Umgang mit offener Polymorphie, finden Sie unter NavBackStackSamples.

Alternative: Speichern in einer ViewModel

Eine weitere Möglichkeit, den Backstack zu verwalten, besteht darin, ihn in einem ViewModel zu speichern. Wenn Sie bei Verwendung von ViewModel oder einem anderen benutzerdefinierten Speicher die Persistenz über das Beenden von Prozessen hinweg beibehalten möchten, müssen Sie Folgendes tun:

  • Schlüssel müssen serialisierbar sein: Wie bei rememberNavBackStack müssen Ihre Navigationsschlüssel serialisierbar sein.
  • Serialisierung und Deserialisierung manuell verarbeiten: Sie sind dafür verantwortlich, die serialisierte Darstellung jedes Schlüssels manuell im persistenten Speicher (z.B. SharedPreferences, einer Datenbank oder einer Datei) zu speichern und daraus zu deserialisieren, wenn Ihre App in den Hintergrund verschoben oder wiederhergestellt wird.

Bereich von ViewModel bis NavEntry Sekunden

ViewModels werden verwendet, um den UI-bezogenen Status bei Konfigurationsänderungen wie Bildschirmdrehungen beizubehalten. Standardmäßig sind ViewModels auf die nächstgelegene ViewModelStoreOwner beschränkt, in der Regel auf Ihre Activity oder Fragment.

Möglicherweise möchten Sie jedoch einen ViewModel auf einen bestimmten NavEntry (d.h. einen bestimmten Bildschirm oder eine bestimmte Zielanwendung) im Backstack und nicht auf das gesamte Activity beschränken. So wird sichergestellt, dass der Status von ViewModel nur beibehalten wird, solange dieser NavEntry Teil des Backstacks ist. Er wird gelöscht, wenn der NavEntry entfernt wird.

Die Add-on-Bibliothek androidx.lifecycle:lifecycle-viewmodel-navigation3 bietet eine NavEntryDecorator, die dies erleichtert. Dieser Dekorator stellt für jede NavEntry ein ViewModelStoreOwner bereit. Wenn Sie ein ViewModel im Inhalt eines NavEntry erstellen (z.B. mit viewModel() in Compose), wird es automatisch auf den Schlüssel dieses NavEntry im Backstack beschränkt. Das bedeutet, dass das ViewModel erstellt wird, wenn das NavEntry dem Backstack hinzugefügt wird, und gelöscht wird, wenn es entfernt wird.

So verwenden Sie NavEntryDecorator, um ViewModels auf NavEntrys einzugrenzen:

  1. Fügen Sie der Datei app/build.gradle.kts die Abhängigkeit androidx.lifecycle:lifecycle-viewmodel-navigation3 hinzu.
  2. Fügen Sie der Liste der entryDecorators das Standard-rememberSaveableStateHolderNavEntryDecorator() hinzu, wenn Sie ein NavDisplay erstellen.
  3. Fügen Sie rememberViewModelStoreNavEntryDecorator() der Liste der entryDecorators hinzu.

NavDisplay(
    entryDecorators = listOf(
        // Add the default decorators for managing scenes and saving state
        rememberSaveableStateHolderNavEntryDecorator(),
        // Then add the view model store decorator
        rememberViewModelStoreNavEntryDecorator()
    ),
    backStack = backStack,
    entryProvider = entryProvider { },
)