withContext — korutina daxilində icra kontekstini dəyişən funksiyadır, müvəqqəti olaraq göstərilən kod bloku üçün thread və ya dispatcher-i dəyişir və nəticəni orijinal kontekstə qaytarır. JetBrains, 2025 məlumatlarına görə, withContext şəbəkə sorğuları və disk əməliyyatları üçün korutinaların ən çox istifadə olunan alətlərindən biridir. Funksiya blok tamamlandıqdan sonra korutinanın orijinal dispatcher-də icranı davam etdirəcəyinə zəmanət verir ki, bu da təsadüfi thread təhlükəsizliyi səhvlərinin qarşısını alır.
Əsas məqamlar
withContext — kotlinx.coroutines paketindən dayandırıcı funksiyadır, verilmiş kod blokunu müəyyən CoroutineContext-də icra edir və nəticəni orijinal kontekstə qaytarır. Funksiyanın imzası belə görünür:
suspend fun withContext (
context: CoroutineContext,
block: suspend CoroutineScope.() -> T
): T
context parametri istənilən CoroutineContext qəbul edir — ən çox standart Dispatchers.IO, Dispatchers.Default və ya Dispatchers.Main-dən biri. Blok məhz bu kontekstdə icra olunur və nəticə withContext-in çağırıldığı yerə qaytarılır.
Lambda tamamlandıqdan sonra withContext zəmanətli şəkildə icranı orijinal dispatcher-ə qaytarır. Bu o deməkdir ki, tərtibatçı fon əməliyyatından sonra əl ilə withContext(Dispatchers.Main) çağırmalı deyil — qayıdış avtomatik baş verir. Bu davranış Kotlin Coroutines spesifikasiyasında 1.3 versiyasından etibarən təsbit edilmişdir.
Android inkişafı — withContext-in əsas tətbiq sahəsidir. Tipik ssenari: ViewModel əsas thread-də korutina işə salır, daxilində şəbəkə sorğusu üçün withContext(Dispatchers.IO) çağırılır və nəticə Main-ə avtomatik qayıtdıqdan sonra UI-ni yeniləmək üçün istifadə olunur. Bu yanaşma MVVM arxitekturasının əsasını təşkil edir və Google tərəfindən korutinalar üzrə rəsmi bələdçidə tövsiyə olunur.
withContext-i başa düşmək üçün CoroutineContext və onun əsas komponenti olan dispatcher-i (Dispatcher) anlamaq lazımdır. Hər bir korutinanın kontekst elementləri dəsti var, onların arasında dispatcher kodun hansı thread və ya thread hovuzunda icra olunacağını müəyyən edir.
| Dispatcher | Təyinat | Hovuz ölçüsü |
|---|---|---|
| Dispatchers.Main | Əsas UI thread (Android, JavaFX, Swing) | 1 (əsas thread) |
| Dispatchers.IO | Disk və şəbəkə əməliyyatları | 64 thread (limit artır) |
| Dispatchers.Default | CPU intensiv hesablamalar | max(2, nüvə sayı) |
| Dispatchers.Unconfined | Sabit thread olmadan | məhdud deyil |
withContext-in yeni korutina yaratmadığını başa düşmək vacibdir — o, mövcud olan üçün yalnız konteksti dəyişir. Bu, onu launch və async-dən fərqləndirən əsas xüsusiyyətdir, çünki onlar yeni korutinalar yaradır. Daxili tətbiq optimallaşdırılıb: tələb olunan kontekst cari ilə üst-üstə düşərsə, dəyişiklik baş vermir — funksiya eyni dispatcher-də icra olunur.
Dispatchers.Main withContext(Dispatchers.Main) daxilində dəyişikliyə səbəb olmur — Kotlin Coroutines kontekstlərin eyniliyini tanıyır və lazımsız əməliyyatı buraxır. Eynilə, artıq Default-da işləyən korutina daxilində withContext(Dispatchers.Default) heç bir əlavə yük yaratmır. Bu optimallaşdırma ContinuationInterceptor-da tətbiq edilmişdir.
Yeni başlayanlar tez-tez withContext-i launch və async ilə qarışdırır, çünki hər üç funksiya korutinalar və kontekstlə işləyir. Lakin onların təyinatı prinsipial olaraq fərqlidir.
| Xüsusiyyət | withContext | launch | async |
|---|---|---|---|
| Yeni korutina yaradır | Xeyr | Bəli | Bəli |
| Nəticə qaytarır | Bəli (T birbaşa) | Xeyr (Job) | Bəli (Deferred<T>) |
| İcra | Ardıcıl | Paralel | Paralel |
| Nəticəni gözləmə | Avtomatik | join() | await() |
| Tipik istifadə | Dispatcher dəyişmə | Fire-and-forget | Paralel hesablamalar |
Fon thread-də bir əməliyyat yerinə yetirib nəticə almaq lazımdırsa — withContext istifadə edin. Bir neçə müstəqil əməliyyatı paralel işə salmaq lazımdırsa — async ilə await istifadə edin. Nəticə lazım deyilsə (loglama, cache yazma) — launch. Google Android arxitekturasında Repository qatı üçün withContext-i üstünlük verilən alət kimi tövsiyə edir.
Kotlin dilində Android tətbiqlərində withContext-in üç praktik ssenarisini nəzərdən keçirək. Hər bir nümunə konkret tapşırığı və düzgün nümunəni göstərir.
ViewModel korutinadan Main-də repository metodunu çağırır. Daxildə withContext(Dispatchers.IO) HTTP sorğusunu icra edir və nəticə avtomatik qaytarılır:
class UserRepository(
private val api: UserApi
) {
suspend fun getUser(id: String): User {
return withContext(Dispatchers.IO) {
api.fetchUser(id)
}
}
}
ViewModel-dəki korutina getUser-i adi dayandırıcı funksiya kimi çağırır — dispatcher-i açıq şəkildə göstərmədən. withContext thread dəyişdirmə detallarını gizlədir.
Bir neçə IO əməliyyatını bir-birinin ardınca yerinə yetirmək lazım olduqda, withContext onları vahid blokda birləşdirir. Bu, hər əməliyyatı ayrıca withContext-ə yerləşdirməkdən daha səmərəlidir:
suspend fun loadUserProfile(id: String): Profile {
return withContext(Dispatchers.IO) {
val user = api.fetchUser(id)
val posts = api.fetchPosts(id)
Profile(user, posts)
}
}
Hər iki əməliyyat Dispatchers.IO-da icra olunur və Profile nəticəsi lazımsız kontekst dəyişikliyi olmadan yaradılıb qaytarılır. Əməliyyatlar müstəqildirsə, paralel icra üçün async istifadə etmək daha yaxşıdır.
Bəzi ssenarilərdə ləğv edilə bilməyən kodu icra etmək lazımdır — məsələn, ekran bağlanarkən vəziyyətin saxlanması. withContext + NonCancellable kombinasiyası bu tapşırığı həll edir:
withContext(Dispatchers.IO + NonCancellable) {
cache.saveState(state)
analytics.logEvent("state_saved")
}
+ operatoru iki kontekst elementini birləşdirir: IO dispatcher-i və NonCancellable bayrağı. Blok ana korutina ləğv edilsə belə icra olunur — bu, yekunlaşdırma əməliyyatları üçün faydalıdır.
withContext-in daxili tətbiqi Continuation mexanizminə — Kotlin korutinalarının mərkəzi abstraksiyasına əsaslanır. Hər bir dayandırma nöqtəsi (suspend point) icra vəziyyətini Continuation obyektində saxlayır və withContext istisna deyil.
Kotlin kompilyatoru withContext-i kotlinx.coroutines-dən withContext metoduna çağırış kimi tərcümə edir, o da daxildə yeni DispatchedContinuation nüsxəsi yaradır. Bu obyekt orijinal Continuation-ı bükür və orada dispatcher-i əvəz edir. Yeni dispatcher cari olandan fərqlidirsə, icra dayandırılır, blok müvafiq thread hovuzuna göndərilir və tamamlandıqdan sonra orijinal kontekstlə bərpa olunur.
withContext korutinanın artıq işlədiyi eyni dispatcher ilə çağırıldıqda, Kotlin fast-path işə salır: blok sinxron şəkildə, DispatchedContinuation yaratmadan və thread hovuzuna ötürmədən icra olunur. Bu, withContext-i eyni kontekstlə təkrar çağırışlarda praktiki olaraq pulsuz edir. JetBrains benchmarklarına görə (kotlinx.coroutines 1.8), fast-path 0.1 μs-dən az müddətdə icra olunur.
Fərqli dispatcher ilə hər withContext çağırışı yeni DispatchedContinuation yaradır və thread dəyişməsi tələb edir — bu, yükdən asılı olaraq 1-dən 5 μs-ə qədər vaxt aparır. Əksər tətbiqlər üçün bu gecikmə nəzərə çarpmır, lakin minlərlə iterasiyası olan dövrülərdə əməliyyatları bir withContext blokunda toplamaq daha yaxşıdır.
Hətta təcrübəli tərtibatçılar withContext ilə işləyərkən səhv edirlər. Dörd ən çox yayılmış problemi və onların qarşısını almaq yollarını nəzərdən keçirək.
Tərtibatçılar tez-tez hər sətri ayrıca withContext-ə yerləşdirir, əməliyyatları bir blokda birləşdirmək əvəzinə. Fərqli dispatcher ilə hər əlavə çağırış əlavə yük yaradır.
Düzgün: ardıcıl IO əməliyyatlarını bir withContext(Dispatchers.IO) { ... } blokunda birləşdirin. Əgər əməliyyatların bir hissəsi CPU intensivdirsə — eyni blok daxilində withContext(Dispatchers.Default) istifadə edin.
withContext kodu ardıcıl icra edir. İki müstəqil şəbəkə sorğusu bir withContext-ə yerləşdirilirsə, onlar bir-birinin ardınca icra olunacaq. Paralellik üçün async + await istifadə edin.
// Ardıcıl — yavaş
withContext(Dispatchers.IO) {
val a = api.fetchA()
val b = api.fetchB()
}
// Paralel — sürətli
coroutineScope {
val a = async { api.fetchA() }
val b = async { api.fetchB() }
println("${a.await()} ${b.await()}")
}
Korutina withContext zamanı ləğv edilərsə, Dispatchers.IO-dakı blok da kəsilir. Nəyin bahasına olursa olsun tamamlanmalı olan əməliyyatlar üçün (verilənlər bazasına yazma, analitika göndərmə) withContext-i NonCancellable ilə birləşdirin.
Heç vaxt View komponentlərini withContext(Dispatchers.IO) daxilində yeniləməyin. withContext bütün blok tamamlanana qədər Main-ə qayıtmır. UI yeniləməsini withContext-in bağlanan mötərizəsindən sonra edin — onda korutina artıq əsas thread-də olacaq.
Tez-tez verilən suallar
withContext — thread-i bloklamayan, mövcud korutina daxilində konteksti dəyişən dayandırıcı funksiyadır. runBlocking — korutinalar və adi kod arasında körpüdür, cari thread-i tamamlanana qədər bloklayır. withContext UI thread-i üçün təhlükəsizdir, runBlocking isə yox.
Xeyr, withContext dayandırıcı funksiyadır, ona görə də onu yalnız başqa dayandırıcı funksiyadan və ya korutinadan (launch/async) çağırmaq olar. Adi funksiyadan withContext çağırılmır — bunun üçün runBlocking və ya CoroutineScope lazımdır.
Kotlin fast-path aktivləşdirir — blok eyni thread-də sinxron şəkildə icra olunur, dəyişiklik olmadan. Əlavə yük 0.1 μs-dən azdır. Bu səhv deyil, lakin belə çağırış lazımsızdır — kodu sadəcə withContext olmadan icra etmək daha yaxşıdır.
withContext daxilindəki istisnalar adi koddakı kimi — try-catch vasitəsilə ötürülür. Blok istisna atarsa, o, ana korutinaya yayılır və idarə edilməzsə onu ləğv edir. withContext daxilində və ya onun ətrafında try-catch istifadə edin.
Xeyr, withContext yeni korutina yaratmır. O, mövcud korutinadan istifadə edir, lakin müvəqqəti olaraq onun kontekstini dəyişir. Bu, onu launch və async-dən fərqləndirir, çünki onlar uşaq korutinalar yaradır. Davranış kotlinx.coroutines mənbə kodu ilə təsdiqlənmişdir.
Xülasə
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