Как стать автором
Обновить

Комментарии 13

вопрос — как запустить StateFlow, чтоб поведение было аналогично LiveData c привязкой к lifecycle?
Например, чтоб результат приходил только в промежутке между onStart/onStop, а если приходит в бекграунде, то постился автоматически при переходе в фореграунд?

Если кратко, то из коробки никак:)
Сам по себе StateFlow никак не связан с Андроид и не знает о жизненных циклах приложения.
Единственное, что может работать — если collect запускать на скоупе либо lifecycleScope либо на скоупе ViewModel. Тогда "отмена" подписки будет происходить сама на onDestroy/onCleared.
А чтобы сделать аналогично LiveData — только самим ручками, либо возможно Гугл сделает что-то с этим сам.

Так LiveData сам по себе тоже ничего не знает о сменах состояния lifecycle, пока ему явно не передашь LifecycleOwner, подписавшись на него вызовом метода observe().
Похожая (но немного перевернутая) ситуация и со StateFlow — сам StateFlow ничего не знает о lifecycle, но подписавшись на него вызовом collect() в рамках «нужного» coroutine-контекста мы обеспечим его правильную работу с lifecycle

Не совсем так.
StateFlow будет "знать" только о create/destroy. Загляните, например, внутрь реализации lifecycleScope.
LiveData же умеет не оповещать если в фоне..StateFlow такое сам не сможет, скойуп не закэнселится в этом случае.

Корутины тоже не стоят на месте) Вот так скоуп будет работать только в started-состоянии. Есть еще whenCreated{} и whenResumed{}
lifecycleScope.launch {
  whenStarted {
    ...
  }
}

И да, StateFlow без coroutine-контекста не знает об изменениях состояния, так же как и LiveData без LifecycleOwner

Есть такое, но тут главное не путаться с "будет работать только в started-состоянии"
Лучше сказать, что стартанёт в started(created/resumed) состоянии, но cancel будет только в destroy.

Не только стартанет. С whenStarted корутина переходит в suspended-состояние, когда lifecycle уходит в onStop, и (State)Flow.collect() перестает получать изменения. Т.е. получаем поведение аналогичное LiveData

Вы правы, давно не заглядывал в исходник Lifecycle.
Только там хитрее. Там используется специальный PausingDispatcher через который проходят все события и они либо выполняются, либо если состояние lifecycle иное, то просто стопается вся очередь и копится, а потом отдаётся.
Думал, там ещё тоже самое что и с ViewModel. Гляну и её, может тоже наконец-то что-то изменилось и в ней..

Спасибо. Т.е получается что данный код более гибкий:
lifecycleScope.launchWhenResumed {
            viewModel.loadingStateFlow
                .collect { Log.d("MainFragment","Loading state: $it") }
        }

Т.к. мы можем наблюдать значения только на скоупе onResume-onPause, или onCreate-onDestroy. А LiveData такого не умеет, так как по умолчанию она умеет слушать только в onStart-onStop.
P.S. ещё заметил интересную особенность StateFlow — когда постишь одно и то же значение несколько раз, то collect будет вызван только 1 раз
да, теперь не сработает костыль с liveData.value = liveData.value, чтобы дернуть подписчиков)
Если вам надо так делать, то скорее всего в коде что-то не так. Скорее всего нужен не стейт, а отдельная команда для обновления чего-то.
Я так и написал — костыль) С другой стороны, если вместо стейта использовать команду, то при пересоздании UI мы не получим предыдущий стейт
ещё заметил интересную особенность StateFlow — когда постишь одно и то же значение несколько раз, то collect будет вызван только 1 раз

Да, там сравнивается через equality.

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Изменить настройки темы

Истории