Категория > Новости > Android: советы и лайфхаки - «Новости»

Android: советы и лайфхаки - «Новости»


2-05-2021, 00:00. Автор: Олег
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.addRepeatingJob из биб­лиоте­ки lifecycle-runtime-ktx вер­сии 2.4.0-alpha01.


lifecycleOwner.addRepeatingJob(Lifecycle.State.STARTED) {
locationProvider.locationFlow().collect {
// Новое местоположение обновляем UI
}
}

Выг­лядит поч­ти так же, как пре­дыду­щий при­мер. Одна­ко в дан­ном слу­чае корути­на будет пол­ностью оста­нов­лена при перехо­де при­ложе­ния в любое сос­тояние, отличное от Lifecycle.State.STARTED, и запуще­на сно­ва при перехо­де в это сос­тояние. Вмес­те с ней будет оста­нов­лен и про­дюсер дан­ных о мес­тополо­жении.


То­го же эффекта мож­но добить­ся, исполь­зуя suspend-фун­кцию repeatOnLifecycle:


lifecycleScope.launch {
lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
locationProvider.locationFlow().collect {
// Новое местоположение обновляем UI
}
}
}

Она удоб­на в тех слу­чаях, ког­да перед сбо­ром дан­ных необ­ходимо выпол­нить опре­делен­ную работу внут­ри suspend-фун­кции.



Перейти обратно к новости