Жизненный цикл в Jetpack Compose   Часть Android Jetpack.

Компоненты, учитывающие жизненный цикл, выполняют действия в ответ на изменение статуса жизненного цикла хост-объекта Activity. Артефакт androidx.lifecycle.compose предоставляет специальные API, которые автоматически очищают ресурсы, когда они покидают экран или когда приложение переходит в фоновый режим.

Ключевые API:

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

Как собирать данные о состоянии жизненного цикла с помощью потоков

Lifecycle предоставляет свойство currentStateFlow, которое возвращает текущее значение Lifecycle.State в виде Kotlin StateFlow. Вы можете забрать этот Flow в качестве State. Это позволяет вашему приложению считывать изменения в жизненном цикле во время композиции.

val lifecycleOwner = LocalLifecycleOwner.current
val stateFlow = lifecycleOwner.lifecycle.currentStateFlow
…
val currentLifecycleState by stateFlow.collectAsState()

Пример выше доступен в модуле lifecycle-common. Метод currentStateAsState() доступен в модуле lifecycle-runtime-compose. Он позволяет легко узнать текущее состояние жизненного цикла с помощью одной строки. Это показано в следующем примере:

val lifecycleOwner = LocalLifecycleOwner.current
val currentLifecycleState = lifecycleOwner.lifecycle.currentStateAsState()

Выполнение кода при событиях жизненного цикла

Вместо того чтобы создавать отдельный класс, реализующий DefaultLifecycleObserver, и вручную добавлять его в жизненный цикл, вы можете объявить логику жизненного цикла встроенной с помощью специальных эффектов. LifecycleEffects позволяет запустить блок, когда в композиции происходит определенное Lifecycle.Event.

LifecycleEventEffect

Используйте LifecycleEventEffect, чтобы запустить блок кода при возникновении определенного события. Это лучше всего подходит для однократных событий, таких как ведение журнала или аналитика, когда не требуется ни успешное выполнение, ни немедленный результат.

@Composable
fun AnalyticsTracker(screenName: String) {
    // Log an event when the app receives ON_RESUME (e.g. comes to foreground)
    LifecycleEventEffect(Lifecycle.Event.ON_RESUME) {
        Analytics.logView(screenName)
    }
}

LifecycleStartEffect

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

Как и другие эффекты Compose (например, LaunchedEffect), LifecycleStartEffect принимает ключи. Когда ключ меняется, блок запускается снова.

Когда происходит событие Lifecycle.Event.ON_STOP или эффект выходит из композиции, выполняется блок onStopOrDispose, чтобы очистить все, что было частью начального блока.

@Composable
fun LocationMonitor(locationManager: LocationManager) {
    // Starts monitoring when ON_START is dispatched
    // Stops monitoring when ON_STOP is dispatched
    //   (or the composable leaves the screen)
    LifecycleStartEffect(locationManager) {
        val listener = LocationListener { location ->
            /* update UI */
        }
        locationManager.requestLocationUpdates(listener)
        // The cleanup block automatically runs on ON_STOP or on disposal
        onStopOrDispose {
            locationManager.removeUpdates(listener)
        }
    }
}

Информацию о других типах побочных эффектов можно найти в статье Побочные эффекты в Compose.

LifecycleResumeEffect

LifecycleResumeEffect работает так же, как и LifecycleStartEffect, но привязан к событию Lifecycle.Event.ON_RESUME. Он также предоставляет блок onPauseOrDispose, который выполняет очистку при отправке ON_PAUSE или когда composable-функция покидает экран.

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

@Composable
fun CameraPreview(cameraController: CameraController) {
    LifecycleResumeEffect(cameraController) {
        cameraController.startPreview()

        onPauseOrDispose {
            cameraController.stopPreview()
        }
    }
}

Доступ к LifecycleOwner

В Compose LifecycleOwner неявно доступен через CompositionLocal с именем LocalLifecycleOwner. По умолчанию владельцем является корневой хост иерархии композиции.

val lifecycleOwner = LocalLifecycleOwner.current

Для многих приложений достаточно проверить этого владельца по умолчанию или передать его эффектам, учитывающим жизненный цикл. Однако для пользовательской навигации или сложных макетов может потребоваться создать собственный LifecycleOwner, чтобы ограничить состояния жизненного цикла определенными разделами пользовательского интерфейса. Например, библиотеки навигации (такие как Navigation 3) делают это автоматически, чтобы у каждого отдельного экрана был свой жизненный цикл.

Создание пользовательского LifecycleOwner

rememberLifecycleOwner() API позволяет создавать и запоминать пользовательские LifecycleOwner. Это особенно полезно для таких компонентов, как HorizontalPager, когда нужно, чтобы только видимая страница была RESUMED, а для соседних страниц, которые не видны на экране, задано значение maxState STARTED.

val pagerState = rememberPagerState(pageCount = { 10 })

HorizontalPager(state = pagerState) { pageNum ->
    val pageLifecycleOwner = rememberLifecycleOwner(
        maxState = if (pagerState.settledPage == pageNum) {
            Lifecycle.State.RESUMED
        } else {
            Lifecycle.State.STARTED
        }
    )

    CompositionLocalProvider(LocalLifecycleOwner provides pageLifecycleOwner) {
        // Your pages here. Their lifecycle-aware components respect the
        // custom maxState defined above.
    }
}

Дополнительную информацию о CompositionLocal можно найти в статье Локальные данные с CompositionLocal.

Рекомендации по работе с компонентами, учитывающими жизненный цикл

  • Контроллеры пользовательского интерфейса должны быть как можно более простыми. Они не должны пытаться получить собственные данные. Вместо этого используйте ViewModel и наблюдайте за объектом StateFlow, чтобы отразить изменения в пользовательском интерфейсе.
  • Попробуйте написать пользовательские интерфейсы, управляемые данными, в которых контроллер пользовательского интерфейса отвечает за обновление пользовательского интерфейса по мере изменения данных или уведомляет о действиях пользователя обратно в ViewModel.
  • Поместите логику данных в класс ViewModel. ViewModel должен служить связующим звеном между контроллером пользовательского интерфейса и остальной частью приложения. Однако будьте осторожны: ViewModel не отвечает за получение данных (например, из сети). Вместо этого ViewModel должен вызывать соответствующий компонент для получения данных, а затем передавать результат обратно контроллеру пользовательского интерфейса.
  • Используйте сопрограммы Kotlin для управления длительными задачами и другими операциями, которые могут выполняться асинхронно.
  • Логика запуска и остановки должна находиться внутри composable-функции, которой она действительно нужна. Таким образом, логика автоматически удаляется, если этот конкретный элемент пользовательского интерфейса удаляется с экрана (например, внутри графа навигации или когда видимость является условной).
  • Используйте collectAsStateWithLifecycle для передачи данных. Не запускайте и не останавливайте сбор данных Flow вручную на основе событий жизненного цикла. Вместо этого используйте collectAsStateWithLifecycle, чтобы эффективно преобразовывать потоки в состояние пользовательского интерфейса. Это позволяет экономить заряд батареи и ресурсы, поскольку Flow приостанавливаются, когда приложение работает в фоновом режиме.

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

Примеры использования компонентов с учетом жизненного цикла

Компоненты, учитывающие жизненный цикл, могут значительно упростить управление жизненными циклами в различных случаях. Вот несколько примеров:

  • Переключение между приблизительными и точными уведомлениями о местоположении. Используйте LifecycleStartEffect, чтобы включить точное определение местоположения, когда приложение открыто (ON_START), и автоматически отключить слушатель или переключиться на приблизительное определение местоположения, когда приложение работает в фоновом режиме (ON_STOP).
  • Остановка и запуск буферизации видео. Используйте LifecycleResumeEffect, чтобы отложить воспроизведение видео до тех пор, пока приложение не будет полностью открыто на переднем плане и не станет интерактивным (ON_RESUME), а также чтобы приостановить воспроизведение и освободить ресурсы, когда приложение работает в фоновом режиме (ON_PAUSE).
  • Запуск и остановка сетевой трансляции. Используйте collectAsStateWithLifecycle, чтобы наблюдать за непрерывными потоками данных (например, Kotlin Flow из сетевого сокета). Это позволяет получать обновления в реальном времени, когда приложение активно, и автоматически отменять сбор данных, когда оно работает в фоновом режиме.
  • Приостановка и возобновление ресурсоемких задач. Используйте LifecycleResumeEffect, чтобы приостанавливать ресурсоемкие визуальные обновления, когда приложение работает в фоновом режиме, и возобновлять их, когда оно переходит в активный режим.

Безопасная обработка событий ON_STOP

Compose безопасно обрабатывает события ON_STOP.

  • Состояние безопасно. Вы можете в любой момент обновить MutableState (например, с помощью uiState.value = ...), даже если приложение работает в фоновом режиме. Compose ждет, пока приложение станет видимым, чтобы отобразить изменения.
  • Автоматическая очистка. При использовании таких эффектов, как LifecycleStartEffect, блок очистки (onStopOrDispose) запускается точно в момент перехода жизненного цикла в состояние STOPPED. Это позволяет избежать удержания тяжелых ресурсов (например, камеры или местоположения), когда приложение находится в фоновом режиме.

Подробнее MutableState и состоянии в Jetpack Compose…

Дополнительные ресурсы

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

Просмотр контента