Жизненный цикл Android-приложений

Обложка поста

Android проектировался таким образом, чтобы расширять возможности пользователей и сделать приложения интуитивно понятными. Например, пользователи приложения могут поворачивать экран, отвечать на уведомления и переключаться на другую задачу. Также у них есть возможность беспрепятственно продолжить работу с приложением после обработки какого-либо события.

Чтобы обеспечить такой UX, вам следует знать, как управлять жизненными циклами компонентов, таких как активности, фрагменты, сервисы, приложение в целом и так далее. Во время работы компонент проходит через состояния, определяемые его жизненным циклом. Система уведомляет о таких переходах через методы обратного вызова (callbacks).

Обратите внимание: Диаграммы описывают поведение в Android P / Jetpack 1.0.

Диаграммы показывают сценарии поведения по умолчанию, если не указано иное.

Жизненный цикл одной активности

Сценарий 1: завершение и повторный запуск приложения

Источники

  • Нажатие кнопки «Back».
  • Вызов метода Activity.finish().

Этот простейший сценарий показывает, что происходит, когда приложение с одной активностью завершается и снова запускается пользователем:

Сценарий 1: приложение завершено и перезапущено

Управление состоянием

  • onSaveInstanceState() не вызывается (активность завершается, поэтому не нужно сохранять её состояние);
  • onCreate() не принимает Bundle, так как предыдущая активность завершилась и не требует восстановления.

Сценарий 2: переход пользователя из приложения

Источники

  • Нажатие кнопки «Home».
  • Переключение на другое приложение (через кнопку «Overview», ответ на уведомление, приём входящего вызова и т. п.).

Сценарий 2: пользователь покидает приложение

В этом случае система остановит активность, но не будет сразу же завершать её.

Управление состоянием

Когда активность входит в остановленное состояние, система вызывает onSaveInstanceState() для сохранения состояния приложения на случай, если она завершит процесс впоследствии.

Предполагая, что процесс всё же не будет завершён, активность вместе со своим состоянием остаётся резидентной в памяти. Когда активность возвращается на передний план, её состояние доступно целиком и нет необходимости в повторной инициализации ранее созданных компонентов.

Сценарий 3: изменение конфигурации

Источники

  • Смена конфигурации, например поворот экрана.
  • Изменение размера окна в мультиоконном режиме.

Сценарий 3: поворот и другие изменения конфигурации

Управление состоянием

Изменения конфигурации вроде поворота и увеличения или уменьшения окна, должны позволять пользователям продолжать работу именно там, где они остановились:

  • предыдущая активность полностью разрушается, но её состояние сохраняется и передаётся новой;
  • в onCreate() и onRestoreInstanceState() передаётся один и тот же Bundle.

Сценарий 4: остановка приложения системой

Источники

  • Вход в мультиоконный режим (API 24+) и потеря фокуса.
  • Другое приложение, частично перекрывающее текущее (диалог покупки, запрос разрешения, сторонний диалог логина).
  • Выбор Intent, например «Поделиться».

Сценарий 4 — остановка приложения системой

Сценарий не относится к следующим случаям:

  • Диалоги в этом же приложении — показ AlertDialog или DialogFragment не приведёт к остановке активности.
  • Уведомления — пользователь, получающий уведомление или открывающий их панель, не остановит активность.

Навигация и стек переходов

Примечание Показанные друг напротив друга группы событий отрабатываются параллельно. Поток выполнения может переключиться с одной группы событий на другую в любой момент времени, поэтому порядок вызовов методов из параллельных групп не определён. Однако последовательный порядок вызовов методов внутри группы гарантирован. Следующие сценарии не применяются к активностям и задачам с кастомным режимом запуска или заданным контекстом задачи (task affinity). За более подробной информацией обратитесь к документации на Android Developers.

Сценарий 1: навигация между активностями

Cценарий 1: завершение и повторный запуск приложения

В этом сценарии при старте новой активности первая останавливается (но не разрушается), что похоже на переход пользователя из приложения (по кнопке «Home»).

После нажатия на кнопку «Back» вторая активность разрушается и завершается.

Управление состоянием

Заметьте, что onSaveInstanceState() вызывается, а onRestoreInstanceState() — нет. Если изменение конфигурации произойдёт в то время, когда вторая активность открыта, то первая разрушится и будет снова создана, когда получит фокус обратно. Вот почему сохранение состояния важно.

Если система убьёт процесс для освобождения ресурсов, будет иметь место другой сценарий, в котором состояние потребуется восстановить.

Сценарий 2: активности в стеке переходов и изменения конфигурации

Сценарий 2: активности в стеке переходов и изменения конфигурации

Управление состоянием

Сохранение состояния важно не только для активности переднего плана. Все активности в стеке должны восстановить свои состояния после изменения конфигурации, чтобы заново построить пользовательский интерфейс.

Кроме того, система может завершить процесс почти в любой момент, так что следует быть готовым восстановить состояние в любой ситуации.

Завершение процесса

Когда Android нуждается в ресурсах, он завершает фоновые приложения.

Сценарий 3: завершение процесса

Управление состоянием

Несмотря на то, что весь стек переходов сохраняется, в целях эффективного использования ресурсов системы активности восстанавливаются только тогда, когда они заново создаются.

Узнать больше можно здесь.

Жизненный цикл фрагментов

В этой части обсудим поведение прикреплённых к активности фрагментов. Не смущайтесь сценария с добавлением фрагмента в стек переходов назад (подробнее о транзакциях фрагментов и стеке переходов здесь).

Сценарий 1: запуск активности с фрагментом и её завершение

Сценарий 1: запуск активности с фрагментом и её завершение

Гарантировано, что вызов onCreate() активности выполнится раньше соответствующих вызовов фрагментов. Однако противолежащие методы обратного вызова, такие как onStart() и onResume(), выполняются параллельно, и поэтому порядок их вызовов не определён. Например, система может выполнить метод onStart() активности перед методом onStart() фрагмента, но выполнить метод onResume() фрагмента перед методом onResume() активности.

Примечание Будьте осторожны при управлении потоками выполнения и избегайте состояния гонки.

Сценарий 2: поворот активности с фрагментом

Сценарий 2: поворот действия с фрагментом

Управление состоянием

Сохранение и восстановление фрагментов очень похоже на сохранение и восстановление активностей. Различие заключается в том, что у фрагментов нет метода onRestoreInstanceState(), но им доступен Bundle в методах onCreate(), onCreateView() и onActivityCreated().

Фрагменты могут быть сохранены, то есть при изменении конфигурации активности будет использоваться один и тот же экземпляр фрагмента.

Сценарий 3: поворот активности с сохранённым фрагментом

Сценарий 3: поворот активности с сохранённым фрагментом

После поворота фрагмент не разрушается и не воссоздаётся, потому что после пересоздания активности используется тот же экземпляр фрагмента — при этом в onActivityCreated() всё ещё доступен объект состояния.

Не рекомендуется применять сохраняемые фрагменты, если они не используются для сохранения данных между изменениями конфигурации (в не UI-фрагменте). Класс ViewModel из библиотеки Architecture Components внутри реализован именно так, но он предоставляет более простой API.

ViewModels, полупрозрачные активности и режимы запуска

Модели представления (ViewModels)

Жизненный цикл модели представления достаточно простой — она имеет всего один метод обратного вызова, который называется onCleared(). Тем не менее, для модели представления есть различие между активностью и фрагментом:

Различие между активностью и фрагментом

Стоит обратить внимание, что инициализация случается всякий раз, когда вы получаете ViewModel, что обычно происходит в onCreate().

Полупрозрачные активности

Из названия понятно, что у полупрозрачной активности полупрозрачный фон (как правило, совсем прозрачный). Таким образом, пользователь видит, что находится под ней.

Когда к теме активности применяется свойство android:windowIsTranslucent, диаграмма немного меняется: фоновая активность никогда не останавливается (только входит в состояние паузы), так что она может продолжать получать обновления UI:

Сравнение обычной и полупрозрачной активностей

Также при возвращении обратно к задаче, обе активности восстанавливаются и запускаются, и только полупрозрачная возобновляется:

Новая полупрозрачная активность: выход из приложения и повторное открытие

Режимы запуска

Рекомендованный способ работы с задачами и стеками переходов в своей основе прост — вы должны следовать поведению по умолчанию. За подробной информацией обратитесь к статье.

Если вам действительно нужен режим SINGLE_TOP, вот диаграмма:

Для сравнения посмотрите на следующей диаграмме режим singleTask (но, скорее всего, вам не нужно его использовать):

Если вы используете Navigation Architecture из Jetpack, то получите выгоду от поддержки Single Top и автоматического искусственного стека переходов.

Перевод статьи «The Android Lifecycle cheat sheet»

Борис Рязанцев