Сохранение и управление состоянием навигации

В следующих разделах описываются стратегии сохранения вашего стека переходов и сохранения состояния, связанного с записями в вашем стеке переходов.

Сохраните свой стек

Обеспечение сохранения состояния навигации вашего приложения при различных событиях жизненного цикла, включая изменения конфигурации и завершение процесса, критически важно для хорошего пользовательского опыта. В 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)
}

Альтернатива: хранение в 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 , выполните следующие действия:

  1. Добавьте зависимость androidx.lifecycle:lifecycle-viewmodel-navigation3 в файл app/build.gradle.kts .
  2. Добавьте rememberSaveableStateHolderNavEntryDecorator() в список entryDecorators при построении NavDisplay .
  3. Добавьте другие декораторы в ваш NavDisplay .

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 { },
)