Сегодня в выпуске: разбираемся с проблемами, возникающими при использовании корутин в Kotlin, превращаем колбэки в suspend-функции, пишем приложение с использованием Kotlin Flow, а также разбираемся, почему нельзя проводить тесты производительности на отладочной сборке, и учимся устанавливать на смартфон сразу отладочную и продакшен‑версии приложения. Ну и как обычно, подборка свежих библиотек.
7 Gotchas When Explore Kotlin Coroutine — статья о проблемах, с которыми можно столкнуться при использовании корутин в Kotlin. Вот наиболее интересные тезисы.
Рассмотрим следующий код:
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) runBlocking(Dispatchers.Main) { Log.d("Track", "${Thread.currentThread()}") Log.d("Track", "$coroutineContext") }}
Выглядит безобидно, но он приведет к фризу приложения. Почему так происходит, подробно описано в статье How runBlocking May Surprise You. Если кратко, то проблема возникает из‑за самого принципа работы runBlocking
. Он запускает новую корутину, а затем блокирует текущий поток. Но если запустить runBlocking
с диспетчером Main
из основного потока, то порожденная корутина окажется в том же потоке и в итоге не сможет получить управление, так как текущий поток будет заблокирован.
По‑хорошему эта проблема решается отказом от использования runBlocking
. Это инструмент для юнит‑тестов, а не для продакшена. Но если очень хочется, можно убрать имя диспетчера из вызова функции:
runBlocking { Log.d("Track", "${Thread.currentThread()}") Log.d("Track", "$coroutineContext")}
Неопытные разработчики считают, что если вызвать метод cancel()
корутины, то она будет завершена сразу. На самом деле это не так и в ряде случаев корутина может успеть полностью отработать, перед тем как обработает сигнал завершения.
Происходит так потому, что корутины реализуют модель кооперативной многозадачности. Когда одна корутина посылает сигнал завершения другой корутине, последняя может либо обработать этот сигнал, либо проигнорировать его. Хорошая новость состоит в том, что все стандартные suspend-функции (yield()
, delay()
, withContext()
и другие) умеют самостоятельно обрабатывать этот сигнал и завершать корутину. Плохая новость — благодаря такой невидимой автоматизации разработчики бывают удивлены, что одни корутины в их коде завершаются почти мгновенно в ответ на cancel()
, а другие продолжают работать.
Проблему решаем так: проверяем значение свойства isActive
между вычислениями и завершаем корутину, если получили значение false
.
Взгляни на следующий код:
@Testfun testingLaunch() { val scope = MainScope() runBlocking { scope.cancel() scope.launch { try { println("Start Launch 2") delay(200) println("End Launch 2") } catch (e: CancellationException) { println("Cancellation Exception") } }.join() println("Finished") }}
Данный код не будет работать. Если вызвать cancel()
на coroutine scope, он становится непригодным для дальнейшего использования. Выхода из этой ситуации два: создать новый scope на месте старого и завершать не сам scope, а все принадлежащие ему корутины:
resultsScope.coroutineContext.cancelChildren()
Suspending over Views — хорошая статья о том, как превратить колбэки в suspend-функции с помощью suspendCancellableCoroutine()
.
В Android колбэки повсюду, и UI фреймворк не исключение. Колбэки используются для всего подряд:
В целом колбеки не очень удобны и могут превратить код в трудноперевариваемую кашу (callback hell). Функции‑расширения из библиотеки android-ktx частично решают эту проблему: преобразуют колбэки на основе классов в лямбды (doOnLayout()
вместо OnLayoutChangeListener()
), но всем нам хотелось бы писать код в последовательном стиле, как и предлагает язык Kotlin и suspend-функции. Но можно ли этого достичь, имея дело с фреймворком Android, написанным с помощью колбэков?
Материалы из последних выпусков становятся доступны по отдельности только через два месяца после публикации. Чтобы продолжить чтение, необходимо стать участником сообщества «Xakep.ru».
Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее
1 год7690 р. |
1 месяц720 р. |
Я уже участник «Xakep.ru»
Читайте также
Последние новости