Coroutines (корутине) — лагане нити Kotlin-а за асинхроно програмирање, доступне кроз библиотеку kotlinx.coroutines. Према JetBrains Kotlin Documentation, 2026, Coroutines омогућавају заустављање извршења функције без блокирања нити, за разлику од традиционалних Thread-ова. Корутине се покрећу на ограниченом пулу нити, што их чини хиљаду пута лакшима од нативних нити. Kotlin Coroutines су потпуно интегрисане са Android Jetpack, Retrofit, Room и другим популарним библиотекама екосистема Android.
Главно
Coroutines (корутине) — механизам асинхроног програмирања у Kotlin-у, имплементиран у библиотеци kotlinx.coroutines. За разлику од нити оперативног система, корутине нису везане за одређену нит: могу се зауставити на једној нити и наставити на другој. Једна нит може извршавати хиљаде корутина, пребацујући се између њих са минималним додатним оптерећењем.
Корутине су се појавиле у Kotlin 1.3 (2018) као експериментална функција и постале стабилне у Kotlin 1.5 (2021). Coroutines решавају проблем callback hell слично async/await, али пружају богатији API: канали (Channel), Flow, обрада изузетака у хијерархији Job и директна интеграција са Android Lifecycle.
Према JetBrains (2025), свака корутина троши око 100 бајтова меморије у поређењу са 1+ MB за нативну нит. Ово омогућава покретање милиона корутина у једној апликацији без ризика од OutOfMemoryError. Управо лакоћа корутина их чини пожељним алатом за асинхроност у Android-у.
Свака Kotlin корутина се компајлира у state machine кроз Continuation Passing Style (CPS). Компајлер додаје скривени параметар Continuation свакој suspend функцији. Continuation садржи тачку наставка и све локалне променљиве. Када се корутина заустави, runtime чува Continuation, а када се настави — враћа га на било којој доступној нити из Dispatcher пула.
suspend — кључна реч Kotlin-а која означава функцију као заустављиву. Таква функција се може позвати само из друге suspend функције или из корутине. Унутар suspend функције могу се позивати друге suspend функције било којим редоследом, и свака тачка позива је потенцијална тачка заустављања.
Механика је једноставна: када suspend функција позове другу suspend функцију, она се зауставља у тој тачки, ослобађајући нит. Након завршетка позване функције, runtime наставља извршење са сачуваног места. Ово се назива cooperative cancellation — ниједна нит није блокирана.
Важно: suspend функција није асинхрона подразумевано. Редослед извршења остаје секвенцијалан ако се не користе launch или async. suspend само омогућава функцији да буде заустављена без блокирања нити и да буде део корутинског контекста. Continuation Passing Style је модел компајлације при којем свака suspend функција добија скривени callback Continuation, а компајлер генерише коначни аутомат за управљање заустављањима и наставцима.
CoroutineScope — контекст који дефинише животни циклус корутина. Све корутине морају бити покренуте унутар scope-а. Када се scope откаже (нпр. при завршетку Activity), све његове подређене корутине се аутоматски отказују. Ово спречава цурење позадинских задатака. Android Jetpack пружа готове scope-ове за сваку компоненту: viewModelScope за ViewModel и lifecycleScope за Activity и Fragment, који се аутоматски отказују при уништењу одговарајуће компоненте.
Structured Concurrency — принцип који гарантује да се корутина неће завршити док се све њене подређене корутине не заврше. Хијерархија Job формира стабло: коренска корутина ствара parent job, подређене — child jobs. Отказивање parent job-а се шири на сву децу. Structured Concurrency је фундаментална разлика између корутина и нити.
| Scope | Где се користи | Отказивање |
|---|---|---|
| GlobalScope | Само за daemon задатке | Не отказује се аутоматски |
| viewModelScope | Android ViewModel | При чишћењу ViewModel-а |
| lifecycleScope | Android Activity/Fragment | При уништењу lifecycle-а |
| coroutineScope | Унутар suspend функције | При отказивању родитељског job-а |
Обични Job при грешци једне подређене корутине отказује све siblings. SupervisorJob је изузетак: квар у једној подређеној корутини не утиче на остале. Ово је важно када се више независних задатака извршава паралелно и један може да падне без потребе за отказивањем осталих.
Dispatchers одређују на којим нитима се извршавају корутине. Dispatchers.Main — главна UI нит Android-а. Dispatchers.IO — пул за блокирајуће операције (мрежа, диск). Dispatchers.Default — за CPU интензивне задатке. Dispatchers.Unconfined — креће у тренутној нити, али не гарантује фиксацију на њој. Правилан избор Dispatcher-а је критичан за перформансе: IO задатак на Default-у блокираће пул за израчунавања, а CPU задатак на IO-у створиће додатне нити.
withContext — функција за пребацивање Dispatcher-а унутар корутине. На пример, suspend функција која парсира JSON може се пребацити на Dispatchers.Default током израчунавања и вратити на Dispatchers.Main за ажурирање UI-ја. withContext је најчешће коришћени градитељ у Android развоју.
launch — покреће корутину, враћа Job, не враћа резултат (fire-and-forget). async — покреће корутину, враћа Deferred из којег се може добити резултат кроз await. runBlocking — блокира тренутну нит за извршење корутине (само за тестове и main функције). Избор градитеља зависи од сценарија: launch одговара за догађаје и ажурирања, async — за задатке са резултатом, runBlocking — само за тестове или улазне тачке.
Размотримо три практична сценарија: основна корутина са launch, паралелни позив са async и обрада грешака са SupervisorJob.
viewModelScope.launch покреће корутину у контексту ViewModel-а. При чишћењу ViewModel-а корутина се аутоматски отказује.
class ProfileViewModel : ViewModel() {
fun loadUser() {
viewModelScope.launch(Dispatchers.IO) {
val user = api.fetchUser()
withContext(Dispatchers.Main) {
showUser(user)
}
}
}
}
coroutineScope са async покреће три захтева паралелно. Резултати се прикупљају кроз .await(). Ако било који захтев падне, сви се отказују.
suspend fun loadDashboard(): Dashboard = coroutineScope {
val user = async { api.fetchUser() }
val posts = async { api.fetchPosts() }
val stats = async { api.fetchStats() }
Dashboard(user.await(), posts.await(), stats.await())
}
SupervisorJob омогућава свакој корутини да се заврши независно. Грешка у једном захтеву не отказује остале.
val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
scope.launch {
try { api.fetchUsers() } catch (e: Exception) { log(e) }
}
scope.launch {
try { api.fetchPosts() } catch (e: Exception) { log(e) }
}
Нити (Thread) — примитив оперативног система. Свака нит има сопствени стек (~1 MB) и захтева системски позив за креирање и пребацивање. Корутине — примитив језика, невезан за ОС. Оне користе Continuation за чување стања и пребацују се на нивоу runtime-а без системских позива.
Према Google (2025), коришћење корутина уместо нити смањује потрошњу меморије за позадинске задатке у Android апликацијама за 90–95%. Све модерне Android библиотеке (Retrofit, Room, WorkManager) имају уграђену подршку за корутине кроз suspend функције. Ktor (клијентски HTTP фрејмворк JetBrains-а) је такође у потпуности изграђен на корутинама, пружајући suspend-функције за сваки захтев без callback API-ја. Room подржава корутине кроз suspend-функције у DAO-у, омогућавајући извршавање упита бази података без блокирања главне нити.
Нити остају неопходне за нативни код кроз JNI, блокирајуће CPU интензивне позиве без временског ограничења (рендеровање видеа, симулације) и при интеграцији са C библиотекама. За све остало — корутине.
Често постављана питања
Корутина — заустављива јединица посла која се извршава на постојећој нити. Нит је системски ресурс са сопственим стеком. Корутине су хиљадама пута лакше од нити и не блокирају ресурсе при заустављању.
Dispatchers.IO је намењен за блокирајуће I/O операције (мрежа, датотеке) и може креирати нове нити по потреби. Dispatchers.Default има фиксан пул (број процесорских језгара) за CPU интензивна израчунавања.
Job.cancel() отказује корутину и сву њену децу. За проверу отказивања унутар корутине користите ensureActive() — баца CancellationException ако је корутина отказана.
Да — кроз библиотеку kotlinx-coroutines-rx3. Она пружа функције awaitSingle, awaitFirst и друге за конверзију Observable/Single у suspend функције и обрнуто кроз flowable.
Flow — хладни асинхрони ток података, аналог RxJava Observable-а за корутине. Flow емитује вредности секвенцијално и завршава се изузетком или успехом. Подржава map, filter, catch и друге операторе.
Закључак
Развићемо мобилну апликацију под кључ
IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође