В зависимости от того, куда передается ваше состояние, и от требуемой логики, вы можете использовать разные API для хранения и восстановления вашего состояния пользовательского интерфейса . Для достижения этой цели каждое приложение использует комбинацию API.
Любое приложение Android может потерять состояние пользовательского интерфейса из-за активности или воссоздания процесса. Эта потеря состояния может произойти из-за следующих событий:
- Изменения конфигурации . Действие уничтожается и создается заново, если изменение конфигурации не обрабатывается вручную .
- Смерть процесса, инициированная системой . Приложение работает в фоновом режиме, и устройство освобождает ресурсы (например, память) для использования другими процессами.
Сохранение состояния после этих событий важно для положительного пользовательского опыта. Выбор сохраняемого состояния зависит от уникальных пользовательских потоков вашего приложения. Рекомендуется, по крайней мере, сохранять пользовательский ввод и состояние, связанное с навигацией. Примеры этого включают положение прокрутки списка, идентификатор элемента, о котором пользователь хочет получить более подробную информацию, текущий выбор пользовательских предпочтений или ввод в текстовые поля.
На этой странице перечислены API, доступные для хранения состояния пользовательского интерфейса, в зависимости от того, где находится ваше состояние, и от логики, которая в нем нуждается.
Логика пользовательского интерфейса
Если ваше состояние поднято в пользовательском интерфейсе либо в компонуемых функциях, либо в простых классах-держателях состояния, ограниченных композицией, вы можете использовать rememberSaveable
для сохранения состояния во время активности и воссоздания процесса.
В следующем фрагменте rememberSaveable
используется для хранения одного логического состояния элемента пользовательского интерфейса:
@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
— это логическая переменная, которая сохраняет данные о том, свернуто или развернуто окно чата.
rememberSaveable
сохраняет состояние элемента пользовательского интерфейса в Bundle
с помощью механизма сохранения состояния экземпляра.
Он может автоматически сохранять примитивные типы в пакете. Если ваше состояние хранится в типе, который не является примитивным, например в классе данных, вы можете использовать различные механизмы хранения, такие как использование аннотации Parcelize
, использование API-интерфейсов Compose, таких как listSaver
и mapSaver
, или реализация пользовательского класса сохранения, расширяющего Compose Runtime Saver
сорт. См. раздел «Способы хранения документации состояния» , чтобы узнать больше об этих методах.
В следующем фрагменте API rememberLazyListState
Compose сохраняет LazyListState
, который состоит из состояния прокрутки LazyColumn
или LazyRow
, с помощью rememberSaveable
. Он использует LazyListState.Saver
— настраиваемую заставку, способную сохранять и восстанавливать состояние прокрутки. После воссоздания действия или процесса (например, после изменения конфигурации, например изменения ориентации устройства), состояние прокрутки сохраняется.
@Composable fun rememberLazyListState( initialFirstVisibleItemIndex: Int = 0, initialFirstVisibleItemScrollOffset: Int = 0 ): LazyListState { return rememberSaveable(saver = LazyListState.Saver) { LazyListState( initialFirstVisibleItemIndex, initialFirstVisibleItemScrollOffset ) } }
Лучшая практика
rememberSaveable
использует Bundle
для хранения состояния пользовательского интерфейса, которое используется другими API, которые также записывают в него данные, например вызовы onSaveInstanceState()
в вашей активности. Однако размер этого Bundle
ограничен, и хранение больших объектов может привести к исключениям TransactionTooLarge
во время выполнения. Это может быть особенно проблематично в приложениях с одним Activity
, где один и тот же Bundle
используется во всем приложении.
Чтобы избежать сбоя такого типа, не следует хранить в бандле большие сложные объекты или списки объектов .
Вместо этого сохраните минимально необходимое состояние, например идентификаторы или ключи, и используйте их для делегирования восстановления более сложного состояния пользовательского интерфейса другим механизмам, например постоянному хранилищу .
Эти варианты дизайна зависят от конкретных вариантов использования вашего приложения и того, как ваши пользователи ожидают от него поведения.
Проверка восстановления состояния
Вы можете убедиться, что состояние, сохраненное с помощью rememberSaveable
в элементах Compose, правильно восстанавливается при воссоздании действия или процесса. Для этого существуют специальные API, например StateRestorationTester
. Чтобы узнать больше, ознакомьтесь с документацией по тестированию .
Бизнес-логика
Если состояние вашего элемента пользовательского интерфейса переносится в ViewModel
поскольку это требуется бизнес-логикой, вы можете использовать API-интерфейсы ViewModel
.
Одним из основных преимуществ использования ViewModel
в вашем приложении Android является то, что он бесплатно обрабатывает изменения конфигурации. При изменении конфигурации, уничтожении и воссоздании активности состояние пользовательского интерфейса, переданное в ViewModel
сохраняется в памяти. После воссоздания старый экземпляр ViewModel
прикрепляется к новому экземпляру действия.
Однако экземпляр ViewModel
не переживает смерть процесса, инициированную системой. Чтобы состояние пользовательского интерфейса выдержало это, используйте модуль Saved State для ViewModel , который содержит API SavedStateHandle
.
Лучшая практика
SavedStateHandle
также использует механизм Bundle
для хранения состояния пользовательского интерфейса, поэтому его следует использовать только для хранения простого состояния элемента пользовательского интерфейса .
Состояние пользовательского интерфейса экрана , которое создается путем применения бизнес-правил и доступа к слоям вашего приложения, отличным от пользовательского интерфейса, не должно храниться в SavedStateHandle
из-за его потенциальной сложности и размера. Вы можете использовать различные механизмы для хранения сложных или больших данных, например локальное постоянное хранилище . После воссоздания процесса экран воссоздается с восстановленным переходным состоянием, которое было сохранено в SavedStateHandle
(если таковой имеется), и состояние пользовательского интерфейса экрана создается снова из уровня данных.
API SavedStateHandle
SavedStateHandle
имеет различные API для хранения состояния элемента пользовательского интерфейса, в частности:
Составление State | saveable() |
---|---|
StateFlow | getStateFlow() |
Составление State
Используйте saveable
API SavedStateHandle
для чтения и записи состояния элемента пользовательского интерфейса как MutableState
, чтобы он сохранялся при активности и воссоздании процесса с минимальной настройкой кода.
API saveable
поддерживает примитивные типы «из коробки» и получает параметр stateSaver
для использования пользовательских хранителей, как и rememberSaveable()
.
В следующем фрагменте message
сохраняет типы пользовательского ввода в TextField
:
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) } ) }
Дополнительную информацию об использовании saveable
API см. в документации SavedStateHandle
.
StateFlow
Используйте getStateFlow()
для сохранения состояния элемента пользовательского интерфейса и использования его как потока из SavedStateHandle
. StateFlow
доступен только для чтения, и API требует, чтобы вы указали ключ, чтобы вы могли заменить поток для создания нового значения. С помощью настроенного вами ключа вы можете получить StateFlow
и получить последнее значение.
В следующем фрагменте savedFilterType
— это переменная StateFlow
, в которой хранится тип фильтра, примененный к списку каналов чата в приложении чата:
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 }
Каждый раз, когда пользователь выбирает новый тип фильтра, вызывается setFiltering
. Это сохраняет новое значение в SavedStateHandle
, хранящееся с ключом _CHANNEL_FILTER_SAVED_STATE_KEY_
. savedFilterType
— это поток, отправляющий последнее значение, сохраненное в ключе. filteredChannels
подписывается на поток для выполнения фильтрации каналов.
Дополнительную информацию об API getStateFlow()
см. в документации SavedStateHandle
.
Краткое содержание
В следующей таблице приведены API-интерфейсы, рассмотренные в этом разделе, и указано, когда использовать каждый из них для сохранения состояния пользовательского интерфейса.
Событие | Логика пользовательского интерфейса | Бизнес-логика в ViewModel |
---|---|---|
Изменения конфигурации | rememberSaveable | Автоматический |
Смерть процесса, инициированная системой | rememberSaveable | SavedStateHandle |
Используемый API зависит от того, где хранится состояние, и от требуемой логики. Для состояния, которое используется в логике пользовательского интерфейса , используйте rememberSaveable
. Если состояние, используемое в бизнес-логике , хранится в ViewModel
, сохраните его с помощью SavedStateHandle
.
Вам следует использовать API-интерфейсы пакета ( rememberSaveable
и SavedStateHandle
) для хранения небольших объемов состояния пользовательского интерфейса. Эти данные — минимум, необходимый для восстановления пользовательского интерфейса в предыдущее состояние вместе с другими механизмами хранения. Например, если вы сохраните идентификатор профиля, который просматривал пользователь, в пакете, вы можете получить тяжелые данные, такие как сведения о профиле, с уровня данных.
Дополнительные сведения о различных способах сохранения состояния пользовательского интерфейса см. в общей документации по сохранению состояния пользовательского интерфейса и на странице уровня данных руководства по архитектуре.
{% дословно %}Рекомендуется для вас
- Примечание. Текст ссылки отображается, когда JavaScript отключен.
- Где поднять состояние
- State и Jetpack Compose
- Списки и сетки