В следующих разделах описаны стратегии сохранения стека возврата и хранения состояния, связанного с записями в вашем стеке возврата.
Сохраните свой стек возвратов.
Для обеспечения удобства использования крайне важно, чтобы состояние навигации вашего приложения сохранялось на протяжении различных этапов жизненного цикла, включая изменения конфигурации и завершение процессов. В Navigation 3 вы владеете своим стеком возврата, поэтому нет строгих правил относительно того, как его следует создавать или сохранять. Однако Navigation 3 предлагает удобный способ сохранения стека возврата: rememberNavBackStack .
Используйте rememberNavBackStack
Компонуемая функция rememberNavBackStack предназначена для создания стека возврата, который сохраняется при изменении конфигурации и завершении процесса.
Для корректной работы rememberNavBackStack каждая клавиша в вашем стеке возврата должна соответствовать определенным требованиям:
- Реализуйте интерфейс
NavKey: каждая клавиша в стеке возврата должна реализовывать интерфейсNavKey. Он действует как интерфейс-маркер, сигнализирующий библиотеке о возможности сохранения клавиши. - Используйте аннотацию
@Serializable: помимо реализацииNavKey, ваши ключевые классы и объекты должны быть помечены аннотацией@Serializable.
Следующий фрагмент кода демонстрирует корректную реализацию функции rememberNavBackStack :
@Serializable data object Home : NavKey @Composable fun NavBackStack() { val backStack = rememberNavBackStack(Home) }
Помните о стеке возврата с подтипами NavKey
Компонуемая функция rememberNavBackStack возвращает объект NavBackStack<NavKey> . Если ваше приложение определяет собственный подтип NavKey , от которого наследуются все его клавиши, вы можете сохранить эту типизацию, реализовав пользовательскую функцию remember следующим образом:
@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) }
Дополнительные примеры, в том числе о том, как обрабатывать открытый полиморфизм, см. NavBackStackSamples .
Альтернативный вариант: хранение в ViewModel
Другой подход к управлению стеком обработки событий — хранение его в ViewModel . Для обеспечения сохранения состояния при завершении процесса при использовании ViewModel или любого другого пользовательского хранилища вам необходимо:
- Убедитесь, что ваши клавиши сериализуемы : как и в случае с
rememberNavBackStack, ваши навигационные клавиши должны быть сериализуемы. - Выполняйте сериализацию и десериализацию вручную : вы несете ответственность за ручное сохранение сериализованного представления каждого ключа в постоянное хранилище (например,
SharedPreferences, базу данных или файл) и его десериализацию из него, когда ваше приложение переходит в фоновый режим или восстанавливается.
Ограничение области видимости ViewModel до NavEntry
ViewModels используются для сохранения состояния, связанного с пользовательским интерфейсом, при изменении конфигурации, например, при повороте экрана. По умолчанию ViewModels ограничена ближайшим ViewModelStoreOwner , которым обычно является ваша Activity или Fragment .
Однако, возможно, вам потребуется ограничить область действия ViewModel конкретным элементом NavEntry (например, определенным экраном или пунктом назначения) в стеке возврата, а не всей Activity . Это гарантирует, что состояние ViewModel будет сохраняться только до тех пор, пока этот конкретный NavEntry находится в стеке возврата, и будет очищено при удалении элемента NavEntry из стека.
Библиотека androidx.lifecycle:lifecycle-viewmodel-navigation3 предоставляет декоратор NavEntryDecorator , который упрощает эту задачу. Этот декоратор предоставляет ViewModelStoreOwner для каждого NavEntry . Когда вы создаете ViewModel внутри содержимого NavEntry (например, используя viewModel() в Compose), он автоматически ограничивается ключом этого конкретного NavEntry в стеке возврата. Это означает, что ViewModel создается при добавлении NavEntry в стек возврата и очищается при его удалении.
Чтобы использовать NavEntryDecorator для ограничения области видимости ViewModel до NavEntry , выполните следующие шаги:
- Добавьте зависимость
androidx.lifecycle:lifecycle-viewmodel-navigation3в файлapp/build.gradle.kts. - Добавьте в список
entryDecorators) функциюrememberSaveableStateHolderNavEntryDecorator(), используемую по умолчанию, при созданииNavDisplay. - Добавьте
rememberViewModelStoreNavEntryDecorator()в списокentryDecorators.
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 { }, )