Coroutine Builder — функции Kotlin Coroutines, которые создают и запускают сопрограммы, определяя способ их выполнения. Билдеры launch, async, runBlocking и produce покрывают разные сценарии: от фоновых задач до параллельных вычислений с возвратом результата. По данным JetBrains, 2024, Coroutine Builder является основой корутинной модели, обеспечивая структурированную конкурентность и управление жизненным циклом.
Главное
Coroutine Builder — это функция-расширение Kotlin, которая принимает CoroutineScope и suspend-блок, создавая и запуская новую сопрограмму. Каждый билдер определяет, как корутина будет выполняться: с возвратом результата или без, с блокировкой потока или асинхронно. Билдеры являются точками входа в корутинную модель языка.
Все билдеры работают через CoroutineScope, который управляет жизненным циклом дочерних корутин. При отмене scope все запущенные через него сопрограммы автоматически отменяются — это принцип структурированной конкурентности. Данный подход предотвращает утечки корутин и гарантирует предсказуемое завершение.
import kotlinx.coroutines.*
fun main() = runBlocking {
// Builders run inside CoroutineScope
val job = launch {
delay(1000L)
println("World!")
}
println("Hello,")
job.join()
}
Kotlin предоставляет четыре встроенных билдера сопрограмм: launch, async, runBlocking и produce. Каждый из них имеет свой возвращаемый тип и область применения. Для мобильной разработки под Android основными являются launch и async — они работают неблокирующим образом и интегрируются с архитектурными компонентами.
| Билдер | Возвращаемый тип | Блокировка потока | Сценарий |
|---|---|---|---|
| launch | Job | Нет | Fire-and-forget задачи |
| async | Deferred<T> | Нет | Параллельные вычисления |
| runBlocking | T | Да | Тесты, main-функция |
| produce | ReceiveChannel<E> | Нет | Потоковая передача (deprecated) |
Каждый билдер принимает дополнительные параметры: CoroutineStart (стратегия запуска), CoroutineContext (диспетчер, исключения) и именованный блок кода. По умолчанию корутина запускается немедленно (CoroutineStart.DEFAULT).
launch — наиболее используемый билдер в Android-разработке. Он запускает сопрограмму, которая не возвращает результат, и возвращает объект Job для управления её жизненным циклом. Это идеальный выбор для операций, где нужен только побочный эффект: сохранение в БД, отправка аналитики, обновление UI.
Билдер launch принимает CoroutineScope, необязательный CoroutineContext и suspend-блок. Возвращаемый Job позволяет отменить корутину, дождаться её завершения или проверить статус.
val scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
val job: Job = scope.launch(CoroutineStart.LAZY) {
val data = fetchFromNetwork()
saveToDatabase(data)
}
job.start()
job.join()
Параметр CoroutineStart.LAZY откладывает запуск до явного вызова start() или join(). Это полезно для отложенной инициализации и условного запуска. Для стандартного немедленного запуска используется CoroutineStart.DEFAULT или параметр опускается.
async — билдер, возвращающий Deferred<T> — асинхронное обещание результата. Вызов await() приостанавливает корутину до получения результата, не блокируя поток. Это основной механизм для параллельных задач в корутинах Kotlin.
async особенно эффективен, когда нужно выполнить несколько независимых операций одновременно. В отличие от последовательного вызова suspend-функций, async запускает корутины параллельно, сокращая общее время выполнения.
suspend fun fetchUserData(): UserData {
val deferred1 = CoroutineScope(Dispatchers.IO).async { api.getProfile() }
val deferred2 = CoroutineScope(Dispatchers.IO).async { api.getSettings() }
val deferred3 = CoroutineScope(Dispatchers.IO).async { api.getNotifications() }
return UserData(
profile = deferred1.await(),
settings = deferred2.await(),
notifications = deferred3.await()
)
}
Deferred наследуется от Job, поэтому async поддерживает все операции жизненного цикла: отмену, ожидание завершения, обработку исключений. При отмене scope дочерние Deferred корутины отменяются автоматически.
runBlocking — единственный билдер, который блокирует текущий поток до завершения корутины. Он создаёт новый CoroutineScope и запускает переданную сопрограмму, блокируя вызывающий поток. Используется в точках входа main(), в тестах и при интеграции с blocking-кодом.
runBlocking оправдан в трёх сценариях: точка входа приложения (main), юнит-тесты suspend-функций и интеграция с callback-based библиотеками, где нельзя использовать suspend. В production-коде Android использование runBlocking на главном потоке категорически не рекомендуется.
class CoroutineTest {
@Test
fun `test suspend function`() = runBlocking {
val result = mySuspendFunction()
assertEquals("expected", result)
}
}
Для тестов рекомендуется использовать kotlinx-coroutines-test с TestCoroutineDispatcher вместо runBlocking — это обеспечивает контроль времени и избегает блокировок в тестовой среде.
Выбор Coroutine Builder зависит от возвращаемого результата и сценария выполнения. Если операция не требует возврата данных — используйте launch. Если нужен результат асинхронной операции — async. runBlocking применяйте только для bridging, а produce замените на Flow для реактивных потоков.
В Android-проектах с использованием Kotlin Coroutines основная пара билдеров — launch и async. launch используется в ViewModel и UseCases для запуска корутин, а async — для параллельных запросов к сети или БД. Современные библиотеки (Ktor, Room) уже поддерживают suspend-функции, что минимизирует необходимость в прямом использовании async.
Часто задаваемые вопросы
launch возвращает Job и не возвращает результат выполнения, а async возвращает Deferred<T> — объект, из которого можно получить результат через await(). launch используется для операций fire-and-forget, async — для задач, возвращающих данные.
Не рекомендуется. runBlocking на главном потоке вызывает ANR и блокирует UI. Используйте lifecycleScope.launch внутри Activity и Fragment — это встроенное решение без блокировок.
Билдер launch возвращает объект Job, который позволяет контролировать жизненный цикл корутины: отменить (cancel), дождаться завершения (join), проверить статус (isActive, isCompleted, isCancelled).
Deferred<T> — это асинхронное обещание результата, возвращаемое билдером async. Он наследуется от Job и добавляет методы await() для получения результата, getCompleted() для неблокирующего доступа и getCompletionExceptionOrNull() для проверки исключения.
Используйте параметр CoroutineStart.LAZY: scope.launch(start = CoroutineStart.LAZY) { ... }. Затем вызовите job.start() или job.join() для фактического запуска. Это полезно для отложенной инициализации и условного выполнения корутин.
Итоги
Мы разработаем мобильное приложение под ключ
IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.
Читайте также