Содержание статьи
Разработчику
Правильный способ сбора Flow из UI-компонентов
A safer way to collect flows from Android UIs — статья о том, как написать с использованием Kotlin Flow асинхронный код, который не будет страдать от проблем перерасхода ресурсов.
Современный подход к написанию приложений для Android выглядит примерно так: слой бизнес‑логики выставляет наружу suspend-функции и продюсеры Flow, а UI-компоненты вызывают suspend-функции или подписываются на Flow и обновляют UI в соответствии с пришедшими данными.
Выглядеть это все может примерно так. Функция — продюсер обновлений местоположения:
fun FusedLocationProviderClient.locationFlow() = callbackFlow<Location> { val callback = object : LocationCallback() { override fun onLocationResult(result: LocationResult?) { result ?: return try { offer(result.lastLocation) } catch(e: Exception) {} } } requestLocationUpdates(createLocationRequest(), callback, Looper.getMainLooper()) .addOnFailureListener { e -> close(e) // In case of exception, close the Flow } awaitClose { removeLocationUpdates(callback) }}
Часть UI-компонента, подписывающаяся на Flow:
lifecycleScope.launchWhenStarted { locationProvider.locationFlow().collect { // Новое местоположение — обновляем UI }}
На первый взгляд — все хорошо. Благодаря использованию lifecycleScope
мы научили код реагировать на изменение жизненного цикла приложения — при уходе приложения в фон обработка значений местоположения будет приостановлена. Но! Продюсер Flow продолжит отправлять данные об обновлении местоположения.
Чтобы избежать такой проблемы, можно либо самостоятельно запускать и останавливать корутину — обработчик Flow при изменении жизненного цикла приложения, либо использовать lifecycleOwner.
из библиотеки lifecycle-runtime-ktx версии 2.4.0-alpha01.
lifecycleOwner.addRepeatingJob(Lifecycle.State.STARTED) { locationProvider.locationFlow().collect { // Новое местоположение — обновляем UI }}
Выглядит почти так же, как предыдущий пример. Однако в данном случае корутина будет полностью остановлена при переходе приложения в любое состояние, отличное от Lifecycle.
, и запущена снова при переходе в это состояние. Вместе с ней будет остановлен и продюсер данных о местоположении.
Того же эффекта можно добиться, используя suspend-функцию repeatOnLifecycle
:
lifecycleScope.launch { lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) { locationProvider.locationFlow().collect { // Новое местоположение — обновляем UI } }}
Она удобна в тех случаях, когда перед сбором данных необходимо выполнить определенную работу внутри suspend-функции.
Продолжение доступно только участникам
Вариант 1. Присоединись к сообществу «Xakep.ru», чтобы читать все материалы на сайте
Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее
Вариант 2. Открой один материал
Заинтересовала статья, но нет возможности стать членом клуба «Xakep.ru»? Тогда этот вариант для тебя! Обрати внимание: этот способ подходит только для статей, опубликованных более двух месяцев назад.
Я уже участник «Xakep.ru»