Dispatchers — кључни појмови, врсте диспечера и како раде

Аутор: IT Sectr Објављено: 2026-06-22 Време читања: 9 мин

Dispatchers у Kotlin Coroutines — компоненте CoroutineContext-а које одређују niti за извршавање корутина: Main (UI nit), IO (мрежа и диск), Default (CPU-интензивни задаци) и Unconfined (тренутна nit). Сваки диспечер управља специјализованим pool-ом niti, оптимизованим за одређени тип посла. Према JetBrains водичу, 2024, избор исправног диспечера је критичан за перформансе и стабилност апликације.

Главне тачке

  • Dispatchers — предефинисани диспечери корутина који управљају расподелом задатака по nitima
  • Dispatchers.Main — Android UI nit, за ажурирање интерфејса и рад са View
  • Dispatchers.IO — pool за мрежне захтеве, читање/писање фајлова и базе података (до 64 niti)
  • Dispatchers.Default — pool за CPU-интензивне задатке према броју језгри процесора
  • Dispatchers.Unconfined — наслеђивање тренутне niti, без пребацивања

Шта су Dispatchers?

Dispatchers — имплементације интерфејса CoroutineDispatcher, које су елементи CoroutineContext-а. Оне одређују на којој niti или pool-у niti ће се корутина извршавати. При креирању корутине кроз launch или async, диспечер може бити прослеђен као први параметар: launch(Dispatchers.IO) { ... }. Ако диспечер није наведен, наслеђује се из спољашњег CoroutineScope-а.

Kotlin пружа четири уграђена диспечера: Main, IO, Default, Unconfined. Сваки диспечер користи сопствени pool niti, оптимизован за одређени тип операција. Исправан избор диспечера одређује перформансе апликације: грешка у избору доводи до кашњења интерфејса, неискоришћености CPU језгри или неефикасног трошења niti.

ДиспечерPool nitiМакс. nitiПримена
Dispatchers.MainЈедна (UI)1Ажурирање UI, LiveData, View
Dispatchers.IOIO pool64 (limitedParallelism)Мрежа, фајлови, базе података
Dispatchers.DefaultCPU poolN језгриСортирање, парсирање, израчунавања
Dispatchers.UnconfinedТренутна nitN/AПосредне операције, тестови

Dispatchers.Main: UI nit

Dispatchers.Main — диспечер који извршава корутине на главној Android niti. Намењен је операцијама везаним за интерфејс: ажурирање TextView, позивање notifyDataSetChanged, рад са LiveData и StateFlow. У Android-у овај диспечер је имплементиран кроз Handler (Looper.getMainLooper()).

kotlin
// Исправно пребацивање на Main за UI ажурирања
viewModelScope.launch(Dispatchers.IO) {
    val data = repository.fetchData()
    withContext(Dispatchers.Main) {
        _uiState.value = data
    }
}

Ако је корутина већ на Main диспечеру, додатни withContext(Dispatchers.Main) не ствара додатни overhead — диспечер проверава тренутну nit и прескаче пребацивање. withContext је пожељан начин пребацивања између диспечера.

Dispatchers.IO: мрежа и диск операције

Dispatchers.IO — диспечер оптимизован за операције улаза-излаза: HTTP захтеви (Ktor, OkHttp), читање и писање фајлова, рад са Room или SQLDelight. Користи pool од 64 niti подразумевано, скалабилан под оптерећењем. Сваки нови IO захтев може креирати додатну nit до достизања лимита.

Ограничење паралелизма

За контролу броја истовремених IO операција користите limitedParallelism(). Ова функција креира нови диспечер са ограничењем броја паралелних niti, спречавајући исцрпљивање pool-а при масовним операцијама.

kotlin
val limitedIo = Dispatchers.IO.limitedParallelism(4)

// Учитај 100 фајлова са limit-ом од 4 конкурентне операције
coroutineScope {
    val files = (1..100).map { index ->
        async(limitedIo) {
            downloadFile("file_$index")
        }
    }
    files.awaitAll()
}

Користите IO диспечер за све операције где корутина проводи време у чекању (I/O-bound). CPU-интензивни задаци на IO диспечеру су неефикасни — заузимају niti намењене улазу-излазу, смањујући пропусну моћ система.

Dispatchers.Default: CPU-интензивни задаци

Dispatchers.Default — диспечер за рачунске операције које оптерећују процесор: сортирање, филтрирање, парсирање JSON-а (Moshi, Kotlinx Serialization), обрада слика, израчунавања. Величина pool-а је једнака броју језгри процесора (али не мање од 2). Ово обезбеђује максимално оптерећење CPU-а без пребацивања контекста.

kotlin
suspend fun processData(input: List<RawRecord>): List<ProcessedRecord> {
    return withContext(Dispatchers.Default) {
        input
            .parallelStream()
            .map { transform(it) }
            .toList()
    }
}

Немојте користити Dispatchers.Default за IO операције — то ће блокирати niti CPU pool-а које би могле обрађивати рачунске задатке. Подела на IO и Default омогућава оптимално коришћење системских ресурса: IO niti чекају улаз-излаз, CPU niti су стално заузете израчунавањима.

Dispatchers.Unconfined: тренутна nit

Dispatchers.Unconfined — специјални диспечер који не везује корутину ни за један pool. Корутина започиње извршавање у оној niti у којој је позвана launch/async, а након суспензије наставља у niti која је позвала resume. Ово понашање је погодно за посредне операције које не захтевају фиксни контекст.

kotlin
fun main() = runBlocking {
    launch(Dispatchers.Unconfined) {
        println("Пре одлагања: ${Thread.currentThread().getName()}")
        delay(500L)
        println("Након одлагања: ${Thread.currentThread().getName()}")
    }
}

У production коду Dispatchers.Unconfined се ретко користи. Главни случајеви: лаке трансформације пре прослеђивања података другом диспечеру и тестови. За production оптерећење користите експлицитне диспечере — Unconfined је непредвидив, јер nit извршавања зависи од имплементације resume-а.

Како одабрати диспечера

Избор диспечера се одређује типом задатка: UI операције → Main, I/O-bound → IO, CPU-bound → Default, посредне → наслеђивање из scope-а. За Android се препоручује покретање корутине на оном диспечеру где се обавља главни посао, а пре ажурирања UI пребацити се на Main путем withContext.

  • Немојте покретати IO операције на Main диспечеру — то блокира UI
  • Немојте покретати CPU-интензивне задатке на IO диспечеру — то неефикасно троши niti IO pool-а
  • Користите limitedParallelism за контролу паралелизма при масовним IO операцијама
  • Пребацујте диспечера помоћу withContext, а не креирањем новог scope-а
  • Наслеђујте диспечера из scope-а, ако корутина не захтева специфични pool

За сложене сценарије комбинујте диспечере помоћу оператора +: Dispatchers.IO + SupervisorJob() + CoroutineExceptionHandler. Ово ствара CoroutineContext са одређеним диспечером, обрадом грешака и изолованом Job хијерархијом.

Често постављана питања

По чему се разликују Dispatchers.IO и Dispatchers.Default?

Dispatchers.IO користи pool до 64 niti за I/O-bound операције (чекање улаза-излаза), а Dispatchers.Default — pool према броју CPU језгри за рачунске задатке. При недостатку niti, оба pool-а могу делити niti један са другим.

Могу ли да креирам свој диспечер у Kotlin Coroutines?

Да, користите newSingleThreadContext() за једнонитни или newFixedThreadPoolContext() за фиксни pool. За production примењујте limitedParallelism() на бази постојећих диспечера — то је ефикасније од креирања нових pool-ова.

Шта се дешава при позиву Dispatchers.Main у позадинској niti?

Ако Dispatchers.Main није доступан (нпр. у JUnit тесту или позадинском сервису), баца се IllegalStateException. За тестове користите TestCoroutineDispatcher, за позадинске сервисе — Dispatchers.IO или Default.

Како ограничити број паралелних IO операција?

Користите Dispatchers.IO.limitedParallelism(N), где је N максимални број паралелних niti. Ово спречава исцрпљивање pool-а при масовним захтевима и даје контролисани паралелизам.

Када користити Dispatchers.Unconfined?

Dispatchers.Unconfined је погодан за посредне операције: лаке трансформације података пре прослеђивања другом диспечеру, тест сценарије. У Android production коду се не препоручује због неодређене niti извршавања након суспензије.

Резиме

  • Dispatchers — CoroutineContext компоненте које одређују niti извршавања корутина
  • Dispatchers.Main — за Android UI операције, једна главна nit
  • Dispatchers.IO — за мрежне и диск операције, pool до 64 niti
  • Dispatchers.Default — за CPU-интензивна израчунавања, pool према броју језгри
  • Dispatchers.Unconfined — без везивања за nit, за посредне операције
  • withContext — главни механизам пребацивања између диспечера унутар корутине
  • Погрешан избор диспечера води до кашњења UI или неефикасног коришћења ресурса

Развићемо мобилну апликацију под кључ

IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.

Разговарајте о пројекту

Прочитајте такође