Безопасное использование Flows в Jetpack Compose

Рекомендуемый подход подписки Flows — подписка с учетом жизненного цикла. Если вы создаете приложение Android с помощью Jetpack Compose, используйте API collectAsStateWithLifecycle для сбора потоков с учетом жизненного цикла из вашего пользовательского интерфейса. CollectAsStateWithLifecycle позволяет вашему приложению сохранять ресурсы приложения, когда они не нужны, например, когда приложение находится в фоне. Ненужное сохранение ресурсов может повлиять на работоспособность устройства пользователя. К таким ресурсам могут относиться запросы Firebase, обновления местоположения или сети, а также подключения к базе данных.

В статье будет рассмотрены различия collectAsStateWithLifecycle и collectAsState.

collectAsStateWithLifecycle

collectAsStateWithLifecycle — это composable функция, которая собирает значения из Flow и представляет последнее значение State с учетом жизненного цикла. Каждый раз, когда происходит Flow.emit, значение State.value обновляется. Это вызывает рекомпозицию каждого использования State.value в Composition.

По умолчанию collectAsStateWithLifecycle использует Lifecycle.State.STARTED для запуска и остановки сбора значений из потока. Это происходит, когда Lifecycle входит и выходит из целевого состояния. Это состояние жизненного цикла можно настроить в параметре minActiveState.

collectAsStateWithLifecycle по умолчанию отменяет подписку Flow, когда приложение находится в фоновом режиме.

collectAsStateWithLifecycle по умолчанию отменяет подписку Flow, когда приложение находится в фоновом режиме.

В следующем фрагменте показано, как использовать collectAsStateWithLifecycle для сбора поля uiState StateFlow, которое ViewModel предоставила в вашей компонуемой функции:

@Composable
fun AuthorRoute(
  onBackClick: () -> Unit,
  modifier: Modifier = Modifier,
  viewModel: AuthorViewModel = hiltViewModel()
) {
  val uiState: AuthorScreenUiState by viewModel.uiState.collectAsStateWithLifecycle()

  AuthorScreen(
    authorState = uiState.authorState,
    newsState = uiState.newsState,
    modifier = modifier,
    onBackClick = onBackClick,
    onFollowClick = viewModel::followAuthorToggle,
  )
}

Каждый раз, когда uiState AuthorViewModel эмитит новое значение AuthorScreenUiState, AuthorRoute будет рекомпозирован.Чтобы начать использовать API collectAsStateWithLifecycle в своем проекте, добавьте артефакт androidx.lifecycle.lifecycle-runtime-compose в свой проект.

dependencies {
    implementation "androidx.lifecycle:lifecycle-runtime-compose:2.6.0"
}

Под капотом

Под капотом реализация collectAsStateWithLifecycle использует API repeatOnLifecycle, который является рекомендуемым способом сбора потоков в Android с использованием системы View. collectAsStateWithLifecycle избавляет вас от необходимости вводить шаблонный код, показанный ниже, который также собирает потоки с учетом жизненного цикла из составной функции:

@Composable
fun AuthorRoute(...) {
    val lifecycle = LocalLifecycleOwner.current.lifecycle
    val uiState by produceState(
        initialValue = viewModel.uiState.value
        key1 = lifecycle
        key2 = viewModel
    ) {
        lifecycle.repeatOnLifecycle(state = STARTED) {
            viewModel.uiState.collect { value = it }
        }
    }

    AuthorScreen(...)
}

collectAsState

Можно задаться вопросом: если collectAsStateWithLifecycle — самый безопасный способ подписки на Flow из Composable функций в Android, зачем нам теперь нужен API collectAsState? или почему бы не добавить функциональность, учитывающую жизненный цикл, в collectAsState вместо создания нового API?

Жизненный цикл компонуемой функции не зависит от платформы, на которой работает Compose. Как описано в документации Lifecycle of composables page, экземпляры Compsable функций входят в композицию, рекомпозируются 0 или более раз и покидают Composition.

Жизненный цикл экземпляра Composable функции в Composition

Жизненный цикл экземпляра Composable функции в Composition

API collectAsState следует жизненному циклу Composition. Он подписывается на Flow, когда Compsable объект входит в Composition, и прекращает сбор, когда он покидает Composition. CollectAsState — это платформенно-независимый API, который можно использовать для подписки.

Однако при использовании Compose в приложении Android жизненный цикл Android также играет важную роль в том, как следует управлять ресурсами. Даже если Compose останавливает рекомпозиции, пока приложение Android находится в фоновом режиме, collectAsState сохраняет Flow активной. Это делает невозможным освобождение ресурсов остальной частью иерархии.

Оба метода collectAsState и collectAsStateWithLifecycle имеют свое предназначение в Compose. Последний при разработке приложений Android, первый при разработке для других платформ.

Переход с collectAsState на collectAsStateWithLifecycle не вызывает никаких затруднений:

@Composable
fun AuthorRoute(
    onBackClick: () -> Unit,
    modifier: Modifier = Modifier,
    viewModel: AuthorViewModel = hiltViewModel()
) {

  
  
-   val uiState: AuthorScreenUiState by viewModel.uiState.collectAsState()
+   val uiState: AuthorScreenUiState by viewModel.uiState.collectAsStateWithLifecycle()

    AuthorScreen(
        authorState = uiState.authorState,
        newsState = uiState.newsState,
        modifier = modifier,
        onBackClick = onBackClick,
        onFollowClick = viewModel::followAuthorToggle,
    )
}

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

Если вы создаете приложение Android с помощью Jetpack Compose, используйте для этого Composable функцию collectAsStateWithLifecycle.

© Habrahabr.ru