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 {
// Билдери раде унутар CoroutineScope-а
val job = launch {
delay(1000L)
println("Свете!")
}
println("Здраво,")
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. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође