lifecycleScope — что это, привязка к Lifecycle и работа в Android

Автор: IT Sectr Опубликовано: 2026-06-23 Время чтения: 9 мин

lifecycleScope — это встроенный CoroutineScope из библиотеки androidx.lifecycle, который привязан к жизненному циклу Activity, Fragment или любого LifecycleOwner и автоматически отменяет корутины при уничтожении компонента. По данным Google Android Developers, 2025, lifecycleScope позволяет безопасно запускать корутины, связанные с UI-слоем, без риска выполнить код после уничтожения Activity или Fragment. scope автоматически отменяется при переходе LifecycleOwner в состояние DESTROYED.

Главное

  • lifecycleScope — CoroutineScope из lifecycle-runtime-ktx, отменяемый при DESTROYED состоянии Lifecycle
  • lifecycleScope.launch — запуск корутины, которая автоматически отменяется при уничтожении LifecycleOwner
  • launchWhenStarted / launchWhenResumed — устаревшие методы, заменённые на repeatOnLifecycle
  • repeatOnLifecycle — современный API для запуска корутин, следующих за конкретным состоянием Lifecycle
  • Dispatchers.Main.immediate — диспетчер по умолчанию для lifecycleScope

Что такое lifecycleScope в Android?

lifecycleScope — это extension-свойство на интерфейс LifecycleOwner (Activity, Fragment, Service), предоставляющее готовый CoroutineScope, привязанный к полному жизненному циклу компонента. Когда LifecycleOwner достигает состояния DESTROYED, lifecycleScope автоматически отменяет все активные корутины.

kotlin
// In Fragment or Activity
lifecycleScope.launch {
    delay(1000)
    showSnackbar("Hello!")
}

В отличие от viewModelScope, lifecycleScope отменяется при каждом уничтожении LifecycleOwner — включая ротацию экрана. Это делает его идеальным для операций, которые должны жить только пока виден конкретный экран.

Где доступен lifecycleScope

lifecycleScope доступен везде, где есть LifecycleOwner:

  • Activity — AppCompatActivity наследует LifecycleOwner
  • Fragment — Fragment наследует LifecycleOwner
  • LifecycleService — сервис с жизненным циклом
  • ProcessLifecycleOwner — жизненный цикл всего приложения
  • Кастомные LifecycleOwner — любые объекты, реализующие LifecycleOwner

Как работает lifecycleScope: Lifecycle и автоматическая отмена

Механизм автоматической отмены lifecycleScope основан на подписке на события Lifecycle. Когда Lifecycle опускается ниже CREATED в DESTROYED, scope отменяется.

Состояния Lifecycle

СостояниеОписаниеScope активен
CREATEDLifecycleOwner создан, onCreate выполненДа
STARTEDLifecycleOwner видим (onStart)Да
RESUMEDLifecycleOwner на переднем плане (onResume)Да
DESTROYEDLifecycleOwner уничтожен (onDestroy)Нет (scope отменён)

Внутреннее устройство lifecycleScope

lifecycleScope создаётся как CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate) и хранится внутри Lifecycle. При переходе Lifecycle в состояние DESTROYED вызывается scope.cancel(). Механизм реализован через LifecycleEventObserver, который подписывается на события жизненного цикла при первом обращении к scope.

Поведение при ротации

При повороте экрана Activity уничтожается (onDestroy) и создаётся заново. lifecycleScope отменяется вместе со старой Activity, и новый экземпляр scope создаётся для новой Activity. Это принципиальное отличие от viewModelScope, который сохраняется при ротации.

API lifecycleScope: launch, launchWhen и repeatOnLifecycle

Библиотека lifecycle предоставляет несколько способов запуска корутин через lifecycleScope. Рассмотрим эволюцию API от устаревших методов к современным.

lifecycleScope.launch — базовый запуск

Самый простой способ — lifecycleScope.launch { ... }. Корутина запускается немедленно и отменяется при DESTROYED. Однако она может выполнять код даже когда UI невидим (например, в фоне после onStop). Это не всегда желательно.

Устаревшие: launchWhenCreated / launchWhenStarted / launchWhenResumed

Эти методы приостанавливали выполнение корутины, когда Lifecycle опускался ниже указанного состояния, и возобновляли при возврате. Однако они были помечены как @Deprecated в lifecycle-runtime-ktx 2.6.0, потому что:

  • Не отменяли корутину — только приостанавливали
  • Приводили к накоплению висящих корутин, потребляющих память
  • Создавали race condition при быстром переключении состояний

Современный API: repeatOnLifecycle

repeatOnLifecycle — рекомендованный Google способ запуска корутин, синхронизированных с жизненным циклом. Он отменяет и перезапускает корутину каждый раз, когда Lifecycle достигает заданного состояния.

kotlin
lifecycleScope.launch {
    repeatOnLifecycle(Lifecycle.State.STARTED) {
        viewModel.uiState.collect { state ->
            updateUI(state)
        }
    }
}

Корутина, переданная в repeatOnLifecycle, запускается когда Lifecycle достигает STARTED, и отменяется когда он опускается ниже STARTED. При возврате в STARTED корутина перезапускается заново. Это безопасно и эффективно — никакие корутины не висят в паузе.

flowWithLifecycle — для Flow

Для сбора данных из Flow с учётом жизненного цикла существует оператор flowWithLifecycle. Он автоматически останавливает и возобновляет сбор при изменении состояния Lifecycle:

kotlin
viewModel.uiState
    .flowWithLifecycle(lifecycle, Lifecycle.State.STARTED)
    .onEach { state -> updateUI(state) }
    .launchIn(lifecycleScope)

Оператор flowWithLifecycle — наиболее лаконичный способ безопасной подписки на Flow в UI-слое.

Примеры использования lifecycleScope

Рассмотрим три реальных сценария применения lifecycleScope в Android-приложении на Kotlin.

Пример 1: Подписка на Location-обновления только когда экран видим

kotlin
class MapFragment : Fragment() {

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        lifecycleScope.launch {
            repeatOnLifecycle(Lifecycle.State.STARTED) {
                locationProvider.observeLocation().collect { loc ->
                    updateMapMarker(loc)
                }
            }
        }
    }
}

Корутина стартует когда фрагмент становится видимым (STARTED) и отменяется когда он уходит с экрана (STOPPED). Если пользователь переключается на другое приложение, Location-обновления не потребляют батарею.

Пример 2: Запуск анимации при старте фрагмента

kotlin
lifecycleScope.launch {
    repeatOnLifecycle(Lifecycle.State.RESUMED) {
        animateFadeIn(titleView)
        delay(200)
        animateSlideUp(contentView)
    }
}

Анимация запускается только когда фрагмент на переднем плане (RESUMED). Если пользователь сворачивает приложение во время анимации, корутина отменяется, и при возврате анимация запускается заново.

Пример 3: Периодическая синхронизация данных на видимом экране

kotlin
lifecycleScope.launch {
    repeatOnLifecycle(Lifecycle.State.STARTED) {
        while (isActive) {
            syncData()
            delay(30_000L)
        }
    }
}

Данные синхронизируются каждые 30 секунд, но только когда экран видим. isActive проверяет, не отменена ли корутина, что обеспечивает возможность безопасного выхода из цикла при уходе с экрана.

lifecycleScope vs viewModelScope: сценарии применения

Оба scope привязаны к жизненному циклу, но к разным его аспектам. Понимание разницы критически важно для правильной архитектуры Android-приложений.

Ключевое различие

viewModelScope привязан к ViewModel, которая переживает ротацию экрана. lifecycleScope привязан к LifecycleOwner (Activity/Fragment), который при ротации уничтожается и создаётся заново. Это определяет сценарии их применения.

Когда использовать lifecycleScope

  • Подписка на системные события (Location, датчики, камера)
  • Анимации и UI-эффекты, привязанные к конкретному экрану
  • Чанковая загрузка данных с учётом видимости экрана
  • Операции, которые должны прекратиться при уходе с экрана

Когда использовать viewModelScope

  • Загрузка данных из Repository
  • Бизнес-логика, которая должна пережить ротацию
  • Кэширование и обработка данных
  • Любые операции, результат которых нужен после ротации

Комбинированное использование

На практике часто встречается комбинация обоих scope: viewModelScope загружает данные и управляет состоянием, lifecycleScope подписывается на Flow из ViewModel с учётом жизненного цикла экрана. Это разделение ответственности считается best practice в современной Android-разработке.

Типовые ошибки при работе с lifecycleScope

Рассмотрим четыре наиболее частые ошибки, которые допускают разработчики при использовании lifecycleScope.

Ошибка 1: Использование lifecycleScope вместо viewModelScope для загрузки данных

Если запустить загрузку данных в lifecycleScope.launch, при ротации экрана корутина отменится, и данные придётся загружать заново. Используйте viewModelScope для долгоживущих операций. lifecycleScope — только для UI-привязанных задач.

Ошибка 2: Сбор Flow без repeatOnLifecycle

Прямой вызов viewModel.someFlow.collect { ... } внутри lifecycleScope.launch продолжает собирать данные даже когда экран невидим. Это может привести к обновлению UI в фоне и лишним накладным расходам. Всегда используйте repeatOnLifecycle или flowWithLifecycle.

Ошибка 3: Забыли про отмену при уходе с экрана

Хотя lifecycleScope отменяется при DESTROYED, код после точки приостановки (suspend) может не выполниться при внезапной отмене. Не полагайтесь на выполнение post-кода после suspend-вызова, если только не используете NonCancellable.

Ошибка 4: Использование устаревшего launchWhenStarted вместо repeatOnLifecycle

launchWhenStarted и его аналоги не отменяют корутину, а только приостанавливают. Если экран много раз переключается между передним и задним планом, корутина накапливает отложенные вызовы. Переходите на repeatOnLifecycle — это единственный правильный способ синхронизации с Lifecycle.

Часто задаваемые вопросы

В чём разница между lifecycleScope и GlobalScope?

lifecycleScope автоматически отменяется при уничтожении LifecycleOwner. GlobalScope живёт всё время работы приложения. Корутина в lifecycleScope не может обновлять UI после уничтожения компонента, в GlobalScope — может, что приводит к падениям. Всегда используйте lifecycleScope в UI-слое.

Можно ли использовать lifecycleScope в ViewModel?

Нет, ViewModel не является LifecycleOwner, поэтому lifecycleScope в ней недоступен. ViewModel использует viewModelScope. Если код должен выполняться в обоих контекстах — вынесите логику в use case или repository с suspend-функциями.

Что произойдёт при повторном вызове repeatOnLifecycle?

Каждый вызов repeatOnLifecycle создаёт новую корутину, которая запускает блок при достижении указанного состояния Lifecycle. Если вызвать repeatOnLifecycle дважды для одного состояния, оба блока будут запущены независимо. Обычно достаточно одного вызова в onViewCreated.

Можно ли задать кастомный Dispatcher для lifecycleScope?

Непосредственно изменить диспетчер lifecycleScope нельзя — он использует Dispatchers.Main.immediate. Внутри блока корутины можно переключиться на другой диспетчер через withContext. Для тестов используйте TestDispatcher с LifecycleOwner.

lifecycleScope отменяется в onPause или onDestroy?

lifecycleScope отменяется при переходе LifecycleOwner в состояние DESTROYED (после onDestroy). Простые вызовы lifecycleScope.launch не отменяются в onPause или onStop. Для паузы при уходе в фон используйте repeatOnLifecycle(STARTED) или repeatOnLifecycle(RESUMED).

Итоги

  • lifecycleScope — CoroutineScope, привязанный к LifecycleOwner и автоматически отменяемый при DESTROYED через LifecycleEventObserver
  • Dispatchers.Main.immediate — диспетчер по умолчанию, обеспечивающий безопасное обновление UI без лишних переключений
  • repeatOnLifecycle — современный API для запуска корутин в заданном состоянии Lifecycle с автоматической отменой и перезапуском
  • flowWithLifecycle — оператор для безопасного сбора Flow из UI с учётом жизненного цикла
  • launchWhenStarted устарел — вместо приостановки используйте repeatOnLifecycle, который отменяет корутину, а не приостанавливает
  • lifecycleScope vs viewModelScope — lifecycleScope для UI-операций (анимации, Location), viewModelScope для данных и бизнес-логики
  • Ротация экрана — lifecycleScope отменяется при ротации, viewModelScope сохраняется; выбирайте scope в зависимости от задачи

Мы разработаем мобильное приложение под ключ

IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.

Обсудить проект

Читайте также