Android: советы и лайфхаки

Android: советы и лайфхаки

Се­год­ня в выпус­ке: пра­виль­ный спо­соб сбо­ра Flow из UI-ком­понен­тов, советы, как сде­лать код на Kotlin чище, десять полез­ных лай­фха­ков и снип­петов кода на все слу­чаи жиз­ни. А так­же объ­ясне­ние сути PendingIntent и под­борка биб­лиотек для раз­работ­чиков.

 

Разработчику

 

Правильный способ сбора 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.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-фун­кции.