Освой Kotlin играючи

Сайт Александра Климова

Шкодим

/* Моя кошка замечательно разбирается в программировании. Стоит мне объяснить проблему ей - и все становится ясно. */
John Robbins, Debugging Applications, Microsoft Press, 2000

Корутины (Сопрограммы). Часть первая

Корутины предназначены для асинхронного программирования и позволяют отказаться от методов обратного вызова (Callback) и RxJava. Самый простой пример применения - вам нужно загрузить картинку с сервера и показать её в ImageView - это две строчки кода. Проблема в том, что на загрузку требуется время и вторая строчка кода должна выполниться не сразу, а после выполнения первой строчки. Нужен механизм ожидания выполнения задачи, после которой можно продолжать работу.

Код с корутинами будет выглядеть в общем виде следующим образом.


launch(Dispatchers.Main) {
    val image = withContext(Dispatchers.IO) { getImage() } // получить из контекста IO
    imageView.setImageBitmap(image) // Возвращаемся в главный поток
}

Пока функция getImage() выполняется в выделенном пуле потоков IO, главный поток свободен и может выполнять любую другую задачу. Функция withContext() приостанавливает текущую корутину, запущенную через launch(), пока работает getImage(). Как только getImage() выполнит свою задачу, корутина возобновит работу в главном потоке и вызовет imageView.setImageBitmap(image).

Корутины являются аналогом потоков, но более легковесны и проще. Во многих случаях они предпочтительнее потоков, если не требуется интенсивной работы.

Немного теории

Современные операционные системы умеют работать с несколькими потоками в каждом процессе. Поток является абстрактным понятием, создающим иллюзию раздельной работы многих участков кода. Но нужно уметь работать с потоками, чтобы избежать проблем, связанных с кодом, который может блокировать другой код.

В стандартном приложении у вас есть основной поток, в котором мы взаимодействуем с компонентами (кнопки, картинки). Если основной поток заблокирован, то приложение замирает и раздражает пользователя. Особенно ярко проблема выражается, когда приложение требует данных из интернета - это узкое горлышко для приложения. Вам приходится ждать ответа от сервера и приложение вынуждено простаивать.

Другой вариант блокирующего потока - интенсивные вычисления. Даже мощный процессор можно загрузить настолько сложными данными, что он затормозит приложение.

Сами по себе потоки являются тяжёлым ресурсом для системы. В некоторых случаях от потоков нельзя отказаться, других вариантов просто нет. Но у вас есть выбор, какой именно поток заблокировать, чтобы дать свободу другому потоку. Очевидно, не стоит блокировать основной поток приложения. Кроме того, можно вообще не блокировать поток, а позволить ему работать асинхронно, не мешая другому потоку.

Чтобы показать преимущество корутин перед потоками, разработчики из JetBrains приводили следующий пример - Запустим 10 тысяч корутин. Система без труда справится с этой задачей. С таким же количеством потоков программа закроется от нехватки памяти.


(1..10000).forEach {
    GlobalScope.launch {
        val threadName = Thread.currentThread().name
        println("$it printed on thread ${threadName}")
    }
}
Thread.sleep(1000)

В целом, корутины - это блок кода, который выполняется асинхронно без блокировки потока, из которого корутина была запущена. Служит заменой устаревшего AsyncTask или схожих технологий в виду своей эффективности.

Сами по себе корутины не являются чем-то новым, они в той или иной форме уже были в других языках программирования даже в 1960-х годах. Kotlin за кулисами использует многопоточность в корутинах в оптимальном режиме.

К сожалению, порог вхождения в корутины очень высок. При знакомстве на котанов (т.е. на нас) обрушивают просто поток (и тут поток, да что же это такое) информации из непонятных и страшных слов. Без стакана (молока) не разберёшься.

Очень сложно! До свидания!

Дополнительные материалы

Часть вторая. Запуск корутины, повтор, suspend, Job

CoroutineContext, runBlocking, Actor

Scope (область видимости)

Dispatchers/Диспетчеры

Channel

Flow

SharedFlow

StateFlow

async/await

Получить код веб-страницы при помощи корутин

Получить картинку из интернета при помощи корутин

Реклама