Прямые ссылки на публичные уроки для быстрого старта и стабильной индексации lesson-страниц.
Вы уже не раз могли слышать про некие методы жизненного цикла, которые есть у разных компонентов. Чтобы разобраться зачем они были придуманы и как их использовать — давайте взглянем на процесс запуска приложения.
Запуск приложения и отрисовка его элементов — довольно сложный технологический процесс. Под капотом выполняются тысячи строк кода, чтобы вы увидели, например, изображение котика в красивом интерфейсе Телеграм. И всё это должно произойти до того, как что-то будет отображено пользователю: неподготовленные ресурсы могут некорректно отображаться, вызывать торможения или вовсе не работать.
Чтобы разделять эти процессы, придумали определённые состояния. В каждом состоянии Activity происходит своя работа. Их всего 3:
При переходе из одного состояния в другое вся работа по подготовке ресурсов выполняется в специально разработанных системных методах — методах жизненного цикла Activity. Эти методы вызываются системой Android автоматически при наступлении того или иного состояния. Это системные коллбэки.
Коллбэки (или обратные вызовы) — это методы, которые могут быть вызваны в будущем при наступлении определённых условий. Система определяет, когда Activity становится видимой, когда переходит в фон, и вызывает соответствующие методы-коллбэки.
При этом нельзя перескочить через состояние: невозможно из CREATED попасть в RESUMED, минуя VISIBLE. Так же нельзя пропустить вызов соответствующего метода ЖЦ при переходе между состояниями.
Жизненный цикл был придуман, чтобы разработчики могли управлять ресурсами системы в разных состояниях приложения и обеспечивать позитивный опыт для конечного пользователя.
Прежде чем воспроизводить работу каждого метода, познакомимся с логированием и инструментом Logcat в Android Studio — чтобы отслеживать вызовы методов в зависимости от состояния Activity.
Логирование — это способ отслеживания выполнения программы путём вывода сообщений в консоль. Внутри метода onCreate() напишем лог с уровнем INFO:
Log.i("!!!", "${this.componentName.shortClassName} Выполняется метод onCreate()")
В Android для создания логов используется класс Log. Он позволяет выводить различные уровни сообщений: VERBOSE, DEBUG, INFO, WARN, ERROR и ASSERT. Также есть уровень WTF (what a terrible failure). Уровни используются для классификации сообщений по степени важности.
INFO используется для информационных сообщений, отражающих нормальное функционирование приложения. ERROR сообщает об исключениях во время выполнения. Каждый тип имеет свой цвет в консоли.
В качестве тега принято использовать имя класса, но для упрощения фильтрации всех логов под одним ключом можно использовать что-то уникальное. В ходе дебага и разработки это удобно, но в рабочем коде лучше не оставлять такие теги. В качестве сообщения выводим название класса и название метода.
Logcat — инструмент, отображающий журналы сообщений, включая логи приложения. Настройка для работы:
package:mine, чтобы отображались сообщения только из нашего приложения.INFO, чтобы видеть информационные сообщения и выше по важности.В Android уровни логирования располагаются в порядке важности:
VERBOSE (наименее важный)DEBUGINFOWARNERRORASSERT (наиболее важный)Когда мы говорим «выше по важности», мы имеем в виду более критичные уровни. Например, ERROR — более высокий уровень важности по сравнению с INFO. При фильтрации по высокому уровню сообщения низкого приоритета отображаться не будут. Важные сообщения подсвечиваются ярче в Android Studio — от белого цвета до красного.
Метод onCreate() вызывается системой один раз за весь жизненный цикл — когда Activity создана под капотом, но ещё нет никаких признаков её отображения.
В этом методе происходит настройка интерфейса: подключается файл разметки, инициализируются слушатели, наблюдатели, адаптеры и так далее.
Метод onStart() вызывается сразу после onCreate(), когда Activity становится видимой пользователю, но ещё не интерактивна. Интерфейс уже подготовлен и отрисован на экране, но пользователь пока не может с ним взаимодействовать.
В onStart() обычно выполняется настройка компонентов, необходимых для отображения: можно инициализировать сервисы геолокации, Bluetooth или базы данных. Разработчик должен заботиться об экономии ресурсов: если Activity ещё не видна, нет смысла подключать неиспользуемые компоненты раньше времени. Поэтому onStart() — идеальное место для их включения, а onStop() — для отключения.
override fun onStart() { super.onStart() Log.i("!!!", "${this.componentName.shortClassName} Выполняется метод onStart()") }
Метод onResume() вызывается, когда Activity становится полностью активной и получает фокус ввода. Теперь она может обрабатывать пользовательский ввод: касания, нажатия кнопок и т.д.
В onResume() обычно возобновляют работу компоненты, которые были приостановлены: запускаются анимации, воспроизведение видео, обновление данных в реальном времени.
override fun onResume() { super.onResume() Log.i("!!!", "${this.componentName.shortClassName} Выполняется метод onResume()") }
При принудительном закрытии приложения цепочка состояний отрабатывает в обратном порядке, но набор методов будет уже другой.
Обратный порядок начинается с метода onPause(). При принудительном закрытии приложения последовательно вызовутся onPause(), onStop() и onDestroy().
Для демонстрации onPause() можно вызвать системное диалоговое окно с запросом разрешения на доступ к локации — оно откроется поверх Activity, затемняя фон. В этой ситуации Activity переходит из состояния RESUMED в VISIBLE: теряет фокус, взаимодействовать с ней нельзя, но интерфейс остаётся видимым на экране.
Activity на этом этапе может сохранить данные для восстановления при возобновлении: состояние ввода текста, настройки. В onPause() можно отключать слушатели, датчики, анимации, приостанавливать воспроизведение видео и задачи по расписанию. При закрытии диалога состояние Activity вернётся в RESUMED и вызовется onResume().
Метод onStop() вызывается системой, когда Activity перестаёт быть видимой для пользователя, но ещё существует в памяти. Это происходит при переходе на другую Activity, сворачивании приложения или открытии другого приложения, полностью перекрывающего текущую Activity.
onStop() подходит для освобождения ресурсов, связанных с UI, и прекращения долгосрочных операций: отключения сервисов геолокации, остановки обновления данных, сохранения состояния приложения.
override fun onStop() { super.onStop() Log.i("!!!", "${this.componentName.shortClassName} Выполняется метод onStop()") }
При сворачивании приложения последовательно вызываются onPause(), затем onStop(). Activity при этом не уничтожается. Однако из-за нехватки ресурсов система может самостоятельно уничтожить приложение в фоне, поэтому всегда стоит думать о сохранении состояния. При повторном открытии свёрнутого приложения последовательно вызовутся onStart() и onResume().
Метод onDestroy() вызывается перед тем, как Activity полностью уничтожается системой. Он позволяет освободить все оставшиеся ресурсы, отключить слушатели и завершить все связанные операции.
В onDestroy() необходимо очищать ссылки на объекты, чтобы предотвратить утечки памяти. Например, при использовании View Binding нужно обнулить ссылку на биндинг — об этом говорится в официальной документации на примере метода onDestroyView() фрагмента.
При полном закрытии приложения в консоли поочерёдно вызываются onPause(), onStop() и onDestroy(). При повторном запуске весь процесс создания и отображения воспроизводится заново: onCreate(), onStart() и onResume().
Activity уничтожается, если:
finish().Методы жизненного цикла созданы, чтобы разработчики могли переопределять их для управления поведением приложения. Зачем это нужно?
Важно понять: разработчик в Android лишь размещает свой код в коллбэках Activity и просит систему что-то сделать. А система сама решает, когда уничтожить Activity, если потребуется освободить память.
Теперь вы понимаете, когда и зачем вызываются ключевые методы жизненного цикла и какие задачи в них уместны.