Урок по треку Android 8 мин

Жизненный цикл Activity и Fragment за 8 минут

Колбэки, смена конфигурации, смерть процесса, back stack, Lifecycle и lifecycle-aware компоненты.

Жизненный цикл спрашивают на любом Android-собесе, даже если проект целиком на Compose. Важно не только перечислить колбэки, но и объяснить, что происходит при повороте, сворачивании и смерти процесса.

Колбэки Activity

onCreate → onStart → onResume → [работает] → onPause → onStop → onDestroy
  • onCreate(savedInstanceState): один раз на экземпляр. Здесь setContent { } или setContentView, получение ViewModel, подписки. savedInstanceState не null, если Activity восстанавливается.
  • onStart: Activity видна пользователю (но может быть не в фокусе).
  • onResume: на переднем плане, получает ввод. Здесь запускают камеру, датчики.
  • onPause: теряет фокус (сверху диалог-Activity, многооконный режим, начало перехода). Должен быть быстрым: следующая Activity не станет активной, пока он не закончится.
  • onStop: больше не видна. Здесь отпускают тяжёлые ресурсы, останавливают обновления UI.
  • onDestroy: экземпляр уничтожается: пользователь закрыл экран, вызван finish() или пересоздание из-за смены конфигурации.
  • onRestart: перед onStart, если Activity возвращается из остановленного состояния.

Система не гарантирует вызов onDestroy, когда убивает процесс: процесс просто завершается. Сохранять важные данные в onDestroy нельзя.

Переход между двумя Activity

Когда A открывает B: A.onPause → B.onCreate → B.onStart → B.onResume → A.onStop. Поэтому onPause должен быть быстрым, а сохранение данных лучше делать в onStop.

При нажатии «Назад» на B: B.onPause → A.onRestart → A.onStart → A.onResume → B.onStop → B.onDestroy.

Смена конфигурации

Поворот, смена языка, тёмной темы, размера окна по умолчанию пересоздают Activity: onPause → onStop → onDestroy → новый экземпляр onCreate → onStart → onResume. Что переживает:

  • ViewModel: хранится в ViewModelStore, который переносится в новый экземпляр;
  • состояние View с id и всё, что положили в onSaveInstanceState (Bundle);
  • в Compose: rememberSaveable; обычный remember теряется.

Флаг android:configChanges в манифесте отключает пересоздание для указанных изменений, тогда вызывается onConfigurationChanged. Это оправдано для игр и видео, но в обычном приложении маскирует ошибки сохранения состояния. С Android 16 на больших экранах приложения с targetSdk 36 больше не могут запретить поворот и изменение размера, поэтому код должен корректно переживать пересоздание.

Смерть процесса

В фоне система может убить процесс ради памяти. Когда пользователь вернётся, Android пересоздаст стек экранов и передаст сохранённый Bundle. ViewModel при этом не переживёт: она жила в памяти процесса. Выживает только то, что в onSaveInstanceState или SavedStateHandle, и персистентные данные (база, DataStore).

Проверить: свернуть приложение, adb shell am kill <package>, вернуться. Или включить «Don't keep activities» в настройках разработчика (это пересоздание Activity, а не смерть процесса, но много ошибок ловит).

onSaveInstanceState вызывается после onStop (на Android 9+). Bundle хранится в системном процессе и передаётся через Binder, поэтому он должен быть маленьким: превышение лимита транзакции (около 1 МБ на все транзакции процесса) даёт TransactionTooLargeException. Списки и картинки туда не кладут.

Fragment

У фрагмента свой цикл и отдельный цикл view:

onAttach → onCreate → onCreateView → onViewCreated → onStart → onResume
→ onPause → onStop → onDestroyView → onDestroy → onDetach

Фрагмент живёт дольше своей view: при уходе в back stack вызывается onDestroyView, но не onDestroy. Поэтому:

  • подписки, которые трогают view, делают на viewLifecycleOwner, а не на this;
  • ссылку на ViewBinding обнуляют в onDestroyView, иначе утечка всей иерархии view.
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    viewLifecycleOwner.lifecycleScope.launch {
        viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
            viewModel.state.collect { render(it) }
        }
    }
}

Lifecycle-aware компоненты

Lifecycle из AndroidX описывает состояния (INITIALIZED, CREATED, STARTED, RESUMED, DESTROYED) и события (ON_CREATE...ON_DESTROY). Свои компоненты подписываются через DefaultLifecycleObserver и сами реагируют на старт и стоп, вместо того чтобы Activity вызывала их вручную. ProcessLifecycleOwner даёт цикл всего приложения (ушло в фон или вернулось).

Launch modes и задачи

standard (новый экземпляр каждый раз), singleTop (если уже наверху, вызывается onNewIntent), singleTask (один экземпляр в задаче, выше него всё очищается), singleInstance. В Compose-приложениях обычно одна Activity, и навигация внутри неё, поэтому этот вопрос стал реже, но на собесе встречается.

Частые ошибки

  • Хранить состояние в полях Activity и терять его при повороте.
  • Тяжёлая работа в onPause.
  • Подписка на LiveData/Flow во фрагменте через this вместо viewLifecycleOwner: дубли обработчиков после возврата из back stack.
  • Большие объекты в Bundle и TransactionTooLargeException.
  • Путать пересоздание при повороте и смерть процесса.

Что спросят на собесе

  • Перечисли колбэки Activity и что в каком делать.
  • Порядок колбэков при переходе A → B и при повороте.
  • Что переживает поворот, а что смерть процесса?
  • Чем жизненный цикл фрагмента отличается от цикла его view?
  • Зачем viewLifecycleOwner и repeatOnLifecycle?

Закрепите за 5 минут

После урока в тренажёре 3-5 проверочных вопросов, дальше модуль уходит в повторения.