Объект экрана не живёт вечно
Activity представляет одну из основных точек взаимодействия приложения с пользователем. При изменении конфигурации, например повороте, система по умолчанию может уничтожить текущий экземпляр Activity и создать новый. Если счётчик был обычным полем этого объекта и заново получил начальный ноль, прежнее значение потеряется. Пользователь воспринимает происходящее как тот же экран, но для разработчика это уже другой объект. Поэтому правильность одного обработчика ещё не означает правильность поведения приложения целиком.
Отделите значение от его изображения
В учебном примере число на экране является отображением состояния, а не единственным местом его хранения. Нажатие изменяет значение счётчика, после чего интерфейс показывает новое число. При создании экрана нужно сначала получить актуальное состояние и затем отобразить его. Если каждый новый экран безусловно записывает ноль, он уничтожает результат восстановления собственным кодом. Полезно отдельно сформулировать два действия: начать новую попытку по явному выбору пользователя и показать уже начатую попытку после технического пересоздания.
Разные события требуют разных средств
ViewModel помогает удерживать данные интерфейса в памяти при изменениях конфигурации в пределах соответствующего владельца жизненного цикла. Однако завершение процесса системой уничтожает и эти объекты. Для небольших данных, нужных при восстановлении состояния, существует SavedStateHandle и другие механизмы сохранённого состояния. Это не универсальный архив: большие изображения и целые базы данных туда складывать не следует. Если результат должен переживать полное закрытие приложения и оставаться доступным позднее, требуется постоянное хранение, выбранное под характер данных.
Начните с обещания пользователю
Для счётчика посетителей выставки потеря накопленного результата при новом запуске может быть существенной. Для временного счётчика тренировочного примера начало новой попытки с нуля может быть нормальным. Одинаковая кнопка допускает разные требования. Сначала запишите ожидаемое поведение для каждого события, затем выбирайте механизм. Не пытайтесь исправить проблему одним запретом поворота: это меняет пользовательский сценарий и не объясняет, что произойдёт при завершении процесса. Проверка должна различать эти ситуации, даже если внешне обе выглядят как повторное открытие экрана.
Попробуйте на практике
Спроектируйте восстановление учебного счётчика без написания приложения. Понадобятся лист бумаги и три карточки событий.
- Запишите исходное состояние: пользователь сделал семь нажатий, на экране видно 7. Определите, считается ли эта попытка временной или должна сохраняться между запусками.
- На первой карточке опишите пересоздание Activity из-за поворота. Укажите ожидаемое число и место, откуда новый экран его получит.
- На второй карточке опишите завершение фонового процесса системой с последующим восстановлением. Объясните, почему одной ссылки на прежний Java-объект недостаточно.
- На третьей карточке запишите явное начало новой попытки пользователем. Отметьте, когда ноль является правильным результатом, а не потерей данных.
- Для каждой карточки составьте пару: изменение состояния и последующее отображение. Проверьте, нет ли безусловного обнуления при каждом создании экрана.
Как проверить результат. В схеме различаются пересоздание экрана, утрата процесса и намеренный сброс. Для каждого события названы ожидаемое значение и способ его получения, а постоянное хранение не смешано с памятью объекта.
Частые вопросы
ViewModel сохраняет данные навсегда?
Нет. Он имеет определённое время жизни и хранит данные в памяти. Сам по себе он не заменяет сохранение на диск и восстановление после завершения процесса.
Нужно ли хранить в состоянии весь экран?
Обычно полезнее сохранять минимальные данные для восстановления: например, число или идентификатор выбранного объекта. Сам интерфейс можно построить заново по этим данным.
Самостоятельный разбор темы. Содержание конкретной обучающей программы здесь не представлено.