Различные системные операции Android могут повлиять на состояние вашего фрагмента. Чтобы обеспечить сохранение состояния пользователя, фреймворк Android автоматически сохраняет и восстанавливает фрагменты и обратный стек. Поэтому вам нужно убедиться, что любые данные в вашем фрагменте также сохраняются и восстанавливаются.
В следующей таблице перечислены операции, которые приводят к потере состояния фрагмента, а также указано, сохраняются ли различные типы состояния при этих изменениях. В таблице указаны следующие типы состояний:
- Переменные: локальные переменные во фрагменте.
- Состояние представления – любые данные, принадлежащие одному или нескольким представлениям во фрагменте.
- SavedState: данные, присущие этому экземпляру фрагмента, которые должны быть сохранены в
onSaveInstanceState(). - NonConfig – данные, полученные из внешнего источника, например сервера или локального хранилища, или данные, созданные пользователем и отправленные на сервер после фиксации.
Часто переменные обрабатываются так же, как и SavedState, но в следующей таблице они различаются, чтобы продемонстрировать влияние различных операций на каждую из них.
| Операция | Переменные | Просмотр состояния | SavedState | NonConfig |
|---|---|---|---|---|
| Добавлено в обратный стек | ✓ | ✓ | x | ✓ |
| Изменение конфигурации | x | ✓ | ✓ | ✓ |
| Завершение и возобновление процесса | x | ✓ | ✓ | ✓* |
| Удаленный фрагмент не добавлен в обратный стек | x | x | x | x |
| Организатор завершил сеанс | x | x | x | x |
* Состояние NonConfig можно сохранить при завершении процесса с помощью модуля Saved State для ViewModel.
Таблица 1. Различные деструктивные операции с фрагментами и их влияние на разные типы состояний.
Рассмотрим конкретный пример. Рассмотрим экран, на котором генерируется случайная строка, отображается в TextView и предоставляется возможность отредактировать строку перед отправкой другу:
В этом примере предположим, что после нажатия кнопки редактирования приложение показывает представление EditText, в котором пользователь может изменить сообщение. Если пользователь нажмет Отмена, представление EditText должно быть очищено, а его видимость должна быть установлена на View.GONE. Для бесперебойной работы такого экрана может потребоваться управление четырьмя фрагментами данных:
| Данные | Тип | Тип состояния | Описание |
|---|---|---|---|
seed |
Long |
NonConfig | Начальное значение, используемое для случайной генерации нового доброго дела. Генерируется при создании ViewModel. |
randomGoodDeed |
String |
SavedState + Variable | Генерируется при первом создании фрагмента.
randomGoodDeed сохраняется, чтобы пользователи видели одно и то же доброе дело даже после завершения и перезапуска процесса. |
isEditing |
Boolean |
SavedState + Variable | Логический флаг, для которого устанавливается значение true, когда пользователь начинает редактирование.
isEditing сохраняется, чтобы при повторном создании фрагмента область редактирования оставалась видимой. |
| Измененный текст | Editable |
Просмотр состояния (владелец: EditText) |
Отредактированный текст в представлении EditText.
Представление EditText сохраняет этот текст, чтобы пользователь не потерял внесенные изменения. |
Таблица 2. Состояния, которыми должно управлять приложение для генерации случайного текста.
В следующих разделах описывается, как правильно управлять состоянием данных с помощью деструктивных операций.
Состояние представления
Представления отвечают за управление собственным состоянием. Например, если представление принимает пользовательский ввод, оно отвечает за сохранение и восстановление этих данных для обработки изменений конфигурации. Все представления, предоставляемые фреймворком Android, имеют собственную реализацию onSaveInstanceState() и onRestoreInstanceState(), поэтому вам не нужно управлять состоянием представления в вашем фрагменте.
Например, в предыдущем сценарии отредактированная строка хранится в EditText. Объект EditText знает значение отображаемого текста, а также другие сведения, например начало и конец выделенного текста.
Чтобы сохранить состояние представления, ему нужен идентификатор. Этот идентификатор должен быть уникальным в пределах фрагмента и его иерархии View. Представления без идентификатора не могут сохранять свое состояние.
<EditText android:id="@+id/good_deed_edit_text" android:layout_width="match_parent" android:layout_height="wrap_content" />
Как указано в таблице 1, представления сохраняют и восстанавливают свое состояние ViewState во время всех операций, которые не удаляют фрагмент и не уничтожают хост.
SavedState
Фрагмент отвечает за управление небольшими объемами динамического состояния, которые являются неотъемлемой частью его функционирования. Для сохранения легко сериализуемых данных можно использовать Fragment.onSaveInstanceState(Bundle).
Как и в случае с Activity.onSaveInstanceState(Bundle), данные, которые вы помещаете в пакет, сохраняются при изменении конфигурации, завершении процесса и повторном создании и доступны в методах onCreate(Bundle), onCreateView(LayoutInflater, ViewGroup, Bundle) и onViewCreated(View, Bundle) фрагмента.
Продолжая предыдущий пример, randomGoodDeed – это документ, который показывается пользователю, а isEditing – это флаг, определяющий, показывает или скрывает фрагмент EditText. Сохраненное состояние должно быть сохранено с помощью onSaveInstanceState(Bundle), как показано в следующем примере:
Kotlin
override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) outState.putBoolean(IS_EDITING_KEY, isEditing) outState.putString(RANDOM_GOOD_DEED_KEY, randomGoodDeed) }
Java
@Override public void onSaveInstanceState(@NonNull Bundle outState) { super.onSaveInstanceState(outState); outState.putBoolean(IS_EDITING_KEY, isEditing); outState.putString(RANDOM_GOOD_DEED_KEY, randomGoodDeed); }
Чтобы восстановить состояние в onCreate(Bundle), извлеките сохраненное значение из пакета:
Kotlin
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) isEditing = savedInstanceState?.getBoolean(IS_EDITING_KEY, false) randomGoodDeed = savedInstanceState?.getString(RANDOM_GOOD_DEED_KEY) ?: viewModel.generateRandomGoodDeed() }
Java
@Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (savedInstanceState != null) { isEditing = savedInstanceState.getBoolean(IS_EDITING_KEY, false); randomGoodDeed = savedInstanceState.getString(RANDOM_GOOD_DEED_KEY); } else { randomGoodDeed = viewModel.generateRandomGoodDeed(); } }
Как указано в таблице 1, переменные сохраняются, когда фрагмент помещается в стек возврата. Если вы будете обрабатывать их как сохраненное состояние, они не будут удаляться при деструктивных операциях.
NonConfig
Данные NonConfig должны размещаться за пределами фрагмента, например в ViewModel. В предыдущем примере seed (состояние NonConfig) генерируется в ViewModel.
Логика поддержания его состояния принадлежит ViewModel.
Kotlin
public class RandomGoodDeedViewModel : ViewModel() { private val seed = ... // Generate the seed private fun generateRandomGoodDeed(): String { val goodDeed = ... // Generate a random good deed using the seed return goodDeed } }
Java
public class RandomGoodDeedViewModel extends ViewModel { private Long seed = ... // Generate the seed private String generateRandomGoodDeed() { String goodDeed = ... // Generate a random good deed using the seed return goodDeed; } }
Класс ViewModel позволяет сохранять данные при изменении конфигурации, например при повороте экрана, и остается в памяти, когда фрагмент помещается в обратный стек. После завершения процесса и его повторного запуска ViewModel создается заново и генерируется новый seed. Добавление модуля SavedState в ViewModel позволяет ViewModel сохранять простое состояние при завершении и повторном создании процесса.
Дополнительные ресурсы
Дополнительную информацию об управлении состоянием фрагментов можно найти в следующих ресурсах.
Практические работы
- Практическая работа Компоненты, учитывающие жизненный цикл