Освой 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)
К сожалению, порог вхождения в корутины очень высок. При знакомстве на котанов (т.е. на нас) обрушивают просто поток (и тут поток, да что же это такое) информации из непонятных и страшных слов. Без стакана (молока) не разберёшься.
Часть вторая. Запуск корутины, повтор, suspend, Job
CoroutineContext, runBlocking, Actor
Получить код веб-страницы при помощи корутин
Получить картинку из интернета при помощи корутин