Dispatchers Kotlin Coroutines-də — CoroutineContext komponentləri olub, korutinlərin icrası üçün iplikləri təyin edir: Main (UI-ipliyi), IO (şəbəkə və disk), Default (CPU-intensiv tapşırıqlar) və Unconfined (cari iplik). Hər bir dispatçer xüsusi iş növü üçün optimallaşdırılmış ixtisaslaşmış iplik hovuzunu idarə edir. JetBrains, 2024 təlimatına görə, düzgün dispatçer seçimi tətbiqin performansı və sabitliyi üçün vacibdir.
Əsas məqamlar
Dispatchers — CoroutineDispatcher interfeysinin implementasiyaları olub, CoroutineContext elementləridir. Onlar korutinanın hansı iplikdə və ya iplik hovuzunda icra olunacağını təyin edir. launch və ya async vasitəsilə korutina yaradılarkən dispatçer ilk parametr kimi ötürülə bilər: launch(Dispatchers.IO) { ... }. Dispatçer göstərilməzsə, xarici CoroutineScope-dan miras alınır.
Kotlin dörd daxili dispatçer təqdim edir: Main, IO, Default, Unconfined. Hər bir dispatçer müəyyən əməliyyat növü üçün optimallaşdırılmış öz iplik hovuzundan istifadə edir. Düzgün dispatçer seçimi tətbiqin performansını müəyyən edir: səhv seçim interfeys ləngimələrinə, CPU nüvələrinin boş qalmasına və ya ipliklərin səmərəsiz istifadəsinə səbəb olur.
| Dispatçer | İplik hovuzu | Maks. iplik | Tətbiq |
|---|---|---|---|
| Dispatchers.Main | Bir (UI) | 1 | UI yeniləmə, LiveData, View |
| Dispatchers.IO | IO hovuzu | 64 (limitedParallelism) | Şəbəkə, fayllar, verilənlər bazası |
| Dispatchers.Default | CPU hovuzu | N nüvə | Çeşidləmə, parsering, hesablamalar |
| Dispatchers.Unconfined | Cari iplik | Yox | Aralıq əməliyyatlar, testlər |
Dispatchers.Main — Android-in əsas ipliyində korutinləri icra edən dispatçer. İnterfeyslə bağlı əməliyyatlar üçün nəzərdə tutulub: TextView yeniləmə, notifyDataSetChanged çağırışı, LiveData və StateFlow ilə iş. Android-də bu dispatçer Handler (Looper.getMainLooper()) vasitəsilə implementasiya olunub.
// UI yeniləmələri üçün Main-ə düzgün keçid
viewModelScope.launch(Dispatchers.IO) {
val data = repository.fetchData()
withContext(Dispatchers.Main) {
_uiState.value = data
}
}
Korutina artıq Main-dispatçerindədirsə, əlavə withContext(Dispatchers.Main) əlavə yük yaratmır — dispatçer cari ipliyi yoxlayır və keçidi buraxır. withContext dispatçerlər arasında keçid üçün üstünlük verilən üsuldur.
Dispatchers.IO — giriş-çıxış əməliyyatları üçün optimallaşdırılmış dispatçer: HTTP sorğuları (Ktor, OkHttp), faylların oxunması və yazılması, Room və ya SQLDelight ilə iş. O, standart olaraq 64 iplikdən ibarət hovuzdan istifadə edir, yük altında miqyaslanır. Hər yeni IO sorğusu limitə çatana qədər əlavə iplik yarada bilər.
Eyni vaxtda IO əməliyyatlarının sayını idarə etmək üçün limitedParallelism() istifadə edin. Bu funksiya paralel ipliklərin sayını məhdudlaşdıran yeni dispatçer yaradır, kütləvi əməliyyatlarda hovuzun tükənməsinin qarşısını alır.
val limitedIo = Dispatchers.IO.limitedParallelism(4)
// 4 paralel əməliyyat limiti ilə 100 faylı yüklə
coroutineScope {
val files = (1..100).map { index ->
async(limitedIo) {
downloadFile("file_$index")
}
}
files.awaitAll()
}
Korutinanın gözləmədə vaxt keçirdiyi bütün əməliyyatlar (I/O-bound) üçün IO-dispatçerindən istifadə edin. IO-dispatçerində CPU-intensiv tapşırıqlar səmərəsizdir — onlar giriş-çıxış üçün nəzərdə tutulmuş iplikləri tutur, sistemin ötürmə qabiliyyətini azaldır.
Dispatchers.Default — prosessoru yükləyən hesablama əməliyyatları üçün dispatçer: çeşidləmə, filtrləmə, JSON parseringi (Moshi, Kotlinx Serialization), şəkillərin işlənməsi, hesablamalar. Hovuzun ölçüsü prosessor nüvələrinin sayına bərabərdir (lakin 2-dən az deyil). Bu, kontekst dəyişmədən CPU-nun maksimum yüklənməsini təmin edir.
suspend fun processData(input: List<RawRecord>): List<ProcessedRecord> {
return withContext(Dispatchers.Default) {
input
.parallelStream()
.map { transform(it) }
.toList()
}
}
IO əməliyyatları üçün Dispatchers.Default istifadə etməyin — bu, hesablama tapşırıqlarını emal edə biləcək CPU hovuzu ipliklərini bloklayacaq. IO və Default-a bölünmə sistem resurslarından optimal istifadə etməyə imkan verir: IO iplikləri giriş-çıxışı gözləyir, CPU iplikləri daim hesablamalarla məşğuldur.
Dispatchers.Unconfined — korutinanı heç bir hovuza bağlamayan xüsusi dispatçer. Korutina launch/async çağırıldığı iplikdə icraya başlayır və dayandırıldıqdan sonra resume-i çağıran iplikdə bərpa olunur. Bu davranış sabit kontekst tələb etməyən aralıq əməliyyatlar üçün uyğundur.
fun main() = runBlocking {
launch(Dispatchers.Unconfined) {
println("Gecikmədən əvvəl: ${Thread.currentThread().getName()}")
delay(500L)
println("Gecikmədən sonra: ${Thread.currentThread().getName()}")
}
}
Production kodda Dispatchers.Unconfined nadir hallarda tətbiq olunur. Əsas hallar: məlumatları başqa dispatçerə ötürməzdən əvvəl yüngül transformasiyalar və testlər. Production yükü üçün aşkar dispatçerlərdən istifadə edin — Unconfined gözlənilməzdir, çünki icra ipliyi resume implementasiyasından asılıdır.
Dispatçer seçimi tapşırıq növü ilə müəyyən edilir: UI əməliyyatları → Main, I/O-bound → IO, CPU-bound → Default, aralıq → scope-dan miras. Android üçün korutinanı əsas işin yerinə yetirildiyi dispatçerdə işə salmaq, UI yeniləməsindən əvvəl isə withContext vasitəsilə Main-ə keçmək tövsiyə olunur.
Mürəkkəb ssenarilər üçün dispatçerləri + operatoru ilə birləşdirin: Dispatchers.IO + SupervisorJob() + CoroutineExceptionHandler. Bu, müəyyən edilmiş dispatçer, xəta idarəetməsi və təcrid olunmuş Job iyerarxiyası ilə CoroutineContext yaradır.
Tez-tez verilən suallar
Dispatchers.IO I/O-bound əməliyyatları (giriş-çıxış gözləməsi) üçün 64 iplikdək hovuzdan istifadə edir, Dispatchers.Default isə hesablama tapşırıqları üçün CPU nüvələrinin sayına görə hovuzdan. İplik çatışmazlığında hər iki hovuz bir-biri ilə iplikləri bölüşə bilər.
Bəli, tək iplikli üçün newSingleThreadContext() və ya sabit hovuz üçün newFixedThreadPoolContext() istifadə edin. Production üçün mövcud dispatçerlər əsasında limitedParallelism() tətbiq edin — bu, yeni hovuzlar yaratmaqdan daha səmərəlidir.
Dispatchers.Main mövcud deyilsə (məsələn, JUnit testində və ya fon xidmətində), IllegalStateException atılır. Testlər üçün TestCoroutineDispatcher, fon xidmətləri üçün Dispatchers.IO və ya Default istifadə edin.
Dispatchers.IO.limitedParallelism(N) istifadə edin, burada N — maksimum paralel iplik sayıdır. Bu, kütləvi sorğularda hovuzun tükənməsinin qarşısını alır və idarə olunan paralellik təmin edir.
Dispatchers.Unconfined aralıq əməliyyatlar üçün uyğundur: məlumatların başqa dispatçerə ötürülməzdən əvvəl yüngül transformasiyası, test ssenariləri. Android production kodunda tövsiyə edilmir, çünki dayandırmadan sonra icra ipliyi qeyri-müəyyəndir.
Nəticə
Açar təslim mobil tətbiq hazırlayacağıq
IT Sectr 2017-ci ildən startaplar və bizneslər üçün iOS və Android tətbiqləri yaradır. Sizə məsləhət verəcəyik və ən yaxşı həlli təklif edəcəyik.
Həm də oxuyun