withContext: nedir, bağlam değiştirme ve coroutine'lerde çalışma

Yazar: IT Sectr Yayınlanma: 2026-06-22 Okuma süresi: 9 dk

withContext — bir coroutine içinde yürütme bağlamını değiştiren, belirtilen kod bloğu için geçici olarak iş parçacığını veya dağıtıcıyı değiştiren ve sonucu orijinal bağlama döndüren bir fonksiyondur. JetBrains, 2025'e göre, withContext ağ istekleri ve disk işlemleri için en sık kullanılan coroutine araçlarından biridir. Fonksiyon, blok tamamlandıktan sonra coroutine'in orijinal dağıtıcıda yürütmeye devam etmesini garanti ederek kazara iş parçacığı güvenliği hatalarını önler.

Önemli Noktalar

  • withContext — iletilen kod bloğu için CoroutineContext'i değiştiren ve sonucu döndüren askıya alma fonksiyonu
  • Dispatchers.IO — ağ ve disk işlemleri için arka plan iş parçacığına geçmek için tipik argüman
  • Dispatchers.Main — withContext'in blok tamamlandıktan sonra yürütmeyi otomatik olarak geri döndürdüğü orijinal bağlam
  • Sıralı çağrılar — withContext, launch ve async'in aksine kodu sırayla yürüterek işlem sırası üzerinde kontrolü basitleştirir
  • Val sonucu — withContext, await veya join olmadan lambda'nın son satırında return ile doğrudan değer döndürür

Kotlin'de withContext nedir?

withContext, kotlinx.coroutines paketinden bir askıya alma fonksiyonudur; iletilen kod bloğunu belirtilen bir CoroutineContext'te yürütür ve sonucu orijinal bağlama döndürür. Fonksiyon imzası şöyledir:

kotlin
suspend fun  withContext(
    context: CoroutineContext,
    block: suspend CoroutineScope.() -> T
): T

context parametresi herhangi bir CoroutineContext'i kabul eder — en yaygın olarak standart Dispatchers.IO, Dispatchers.Default veya Dispatchers.Main'den biri. Blok bu bağlamda yürütülür ve sonuç, withContext'in çağrıldığı yere döndürülür.

Temel özellik: otomatik geri dönüş

Lambda tamamlandıktan sonra, withContext yürütmeyi garantili bir şekilde orijinal dağıtıcıya geri döndürür. Bu, geliştiricinin arka plan işleminden sonra manuel olarak withContext(Dispatchers.Main) çağırması gerekmediği anlamına gelir — geri dönüş otomatik olarak gerçekleşir. Bu davranış, Kotlin Coroutines spesifikasyonunda sürüm 1.3'ten beri belgelenmiştir.

withContext nerede kullanılır

Android geliştirme, withContext'in ana kullanım alanıdır. Tipik bir senaryo: Bir ViewModel, ana iş parçacığında bir coroutine başlatır, içinde bir ağ isteği için withContext(Dispatchers.IO) çağırır ve Main'e otomatik dönüşten sonra sonuç UI'yi güncellemek için kullanılır. Bu yaklaşım, MVVM mimarisinin temelidir ve Google tarafından resmi coroutine kılavuzunda önerilir.

withContext nasıl çalışır: dağıtıcıları değiştirme

withContext'i anlamak için CoroutineContext ve onun temel bileşeni olan dağıtıcıyı anlamanız gerekir. Her coroutine'in bir dizi bağlam öğesi vardır ve bunlar arasında dağıtıcı, kodun hangi iş parçacığında veya iş parçacığı havuzunda çalıştırılacağını belirler.

withContext için standart dağıtıcılar

DağıtıcıAmaçHavuz boyutu
Dispatchers.MainAna UI iş parçacığı (Android, JavaFX, Swing)1 (ana iş parçacığı)
Dispatchers.IODisk ve ağ işlemleri64 iş parçacığı (sınır büyür)
Dispatchers.DefaultCPU yoğun hesaplamalarmax(2, çekirdek sayısı)
Dispatchers.UnconfinedSabit iş parçacığı yoksınırsız

withContext'in yeni bir coroutine oluşturmadığını anlamak önemlidir — sadece mevcut olanın bağlamını değiştirir. Bu, yeni coroutine'ler oluşturan launch ve async'den temel farktır. withContext'in iç uygulaması optimize edilmiştir: istenen bağlam mevcut bağlamla eşleşirse, değişiklik olmaz — fonksiyon aynı dağıtıcıda yürütülür.

withContext ne zaman iş parçacığını DEĞİŞTİRMEZ

withContext(Dispatchers.Main) içindeki Dispatchers.Main bir değişikliğe neden olmaz — Kotlin Coroutines bağlamların kimliğini tanır ve gereksiz işlemi atlar. Benzer şekilde, zaten Default üzerinde çalışan bir coroutine içindeki withContext(Dispatchers.Default) ek yük oluşturmaz. Bu optimizasyon ContinuationInterceptor içinde uygulanmıştır.

withContext vs launch ve async: ne zaman ne seçilir

Yeni başlayanlar genellikle withContext'i launch ve async ile karıştırır, çünkü her üç fonksiyon da coroutine'ler ve bağlam ile çalışır. Ancak amaçları temelde farklıdır.

Üç fonksiyonun karşılaştırması

ÖzellikwithContextlaunchasync
Yeni coroutine oluştururHayırEvetEvet
Sonuç döndürürEvet (T doğrudan)Hayır (Job)Evet (Deferred<T>)
YürütmeSıralıParalelParalel
Sonuç beklemeOtomatikjoin()await()
Tipik kullanım durumuDağıtıcı değiştirmeAteşle-unutParalel hesaplamalar

Seçim kuralı

Arka plan iş parçacığında bir işlem yürütmeniz ve sonuç almanız gerekiyorsa — withContext kullanın. Birden fazla bağımsız işlemi paralel olarak çalıştırmanız gerekiyorsa — await ile async kullanın. Sonuca ihtiyacınız yoksa (günlükleme, önbellek yazma) — launch kullanın. Google, Android mimarisinde Repository katmanı için withContext'i tercih edilen araç olarak önerir.

withContext ile kod örnekleri

Kotlin Android uygulamalarında withContext kullanımının üç pratik senaryosuna bakalım. Her örnek belirli bir görevi ve doğru deseni gösterir.

Örnek 1: Repository'de ağ isteği

Bir ViewModel, Main üzerindeki bir coroutine'den depo yöntemini çağırır. İçeride, withContext(Dispatchers.IO) bir HTTP isteği yürütür ve sonuç otomatik olarak döndürülür:

kotlin
class UserRepository(
    private val api: UserApi
) {
    suspend fun getUser(id: String): User {
        return withContext(Dispatchers.IO) {
            api.fetchUser(id)
        }
    }
}

ViewModel'deki coroutine, getUser'ı herhangi bir normal askıya alma fonksiyonu gibi çağırır — dağıtıcıyı açıkça belirtmeden. withContext, iş parçacığı değiştirmenin ayrıntılarını gizler.

Örnek 2: İki sıralı arka plan işlemi

Birden fazla IO işlemini art arda yürütmeniz gerektiğinde, withContext bunları tek bir blokta birleştirir. Bu, her işlemi ayrı bir withContext'e sarmaktan daha verimlidir:

kotlin
suspend fun loadUserProfile(id: String): Profile {
    return withContext(Dispatchers.IO) {
        val user = api.fetchUser(id)
        val posts = api.fetchPosts(id)
        Profile(user, posts)
    }
}

Her iki işlem de Dispatchers.IO üzerinde çalışır ve Profile sonucu gereksiz bağlam değişiklikleri olmadan oluşturulur ve döndürülür. İşlemler bağımsızsa, paralel yürütme için async kullanmak daha iyidir.

Örnek 3: NonCancellable ile karma bağlam

Bazı senaryolarda, iptal edilemeyecek kodu yürütmeniz gerekir — örneğin, bir ekranı kapatırken durumu kaydetme. withContext + NonCancellable kombinasyonu bu görevi çözer:

kotlin
withContext(Dispatchers.IO + NonCancellable) {
    cache.saveState(state)
    analytics.logEvent("state_saved")
}

+ operatörü iki bağlam öğesini birleştirir: IO dağıtıcısı ve NonCancellable bayrağı. Blok, üst coroutine iptal edilmiş olsa bile yürütülür — sonlandırma işlemleri için kullanışlıdır.

Perde arkasında neler oluyor: Continuation ve optimizasyonlar

withContext'in iç uygulaması Continuation mekanizmasına dayanır — Kotlin coroutine'lerinin merkezi soyutlamasıdır. Her askıya alma noktası, yürütme durumunu bir Continuation nesnesinde kaydeder ve withContext bir istisna değildir.

withContext, bayt kodu düzeyinde bağlamı nasıl değiştirir

Kotlin derleyicisi, withContext'i kotlinx.coroutines'deki withContext yöntemine bir çağrı olarak çevirir ve bu yöntem dahili olarak yeni bir DispatchedContinuation örneği oluşturur. Bu nesne, orijinal Continuation'ı sarar ve dağıtıcısını değiştirir. Yeni dağıtıcı mevcut olandan farklıysa, yürütme askıya alınır, blok ilgili iş parçacığı havuzuna gönderilir ve tamamlandıktan sonra — orijinal bağlamla devam eder.

Optimizasyon: bağlamlar eşleştiğinde hızlı yol (fast-path)

withContext, coroutine'in zaten üzerinde çalıştığı aynı dağıtıcı ile çağrıldığında, Kotlin hızlı yolu (fast-path) etkinleştirir: blok, DispatchedContinuation oluşturmadan ve iş parçacığı havuzuna göndermeden eşzamanlı olarak yürütülür. Bu, withContext'i aynı bağlamla tekrarlanan çağrılar için neredeyse ücretsiz hale getirir. JetBrains kıyaslamalarına (kotlinx.coroutines 1.8) göre, hızlı yol 0,1 µs'den daha kısa sürede tamamlanır.

Performans hususları

Farklı bir dağıtıcı ile her withContext çağrısı yeni bir DispatchedContinuation oluşturur ve iş parçacığı değiştirme gerektirir — bu, yüke bağlı olarak 1 ila 5 µs sürer. Çoğu uygulama için bu gecikme fark edilmez, ancak binlerce yinelemeli döngüler içinde, işlemleri tek bir withContext bloğunda toplamak faydalıdır.

withContext kullanırken yaygın hatalar

Deneyimli geliştiriciler bile withContext ile çalışırken hatalar yapar. En yaygın dört sorunu ve bunları önleme yollarını inceleyelim.

Hata 1: Gereksiz iç içe withContext

Geliştiriciler genellikle işlemleri tek bir blokta birleştirmek yerine her satırı ayrı bir withContext'e sarar. Farklı bir dağıtıcı ile yapılan her ekstra çağrı ek yük oluşturur.

Doğrusu: sıralı IO işlemlerini tek bir withContext(Dispatchers.IO) { ... } içinde birleştirin. Bazı işlemler CPU yoğunsa — aynı blok içinde withContext(Dispatchers.Default) kullanın.

Hata 2: Paralel görevler için async yerine withContext kullanma

withContext kodu sırayla yürütür. İki bağımsız ağ isteği tek bir withContext'e sarılmışsa, birbiri ardına çalışırlar. Paralellik için async + await kullanın.

kotlin
// Sıralı — yavaş
withContext(Dispatchers.IO) {
    val a = api.fetchA()
    val b = api.fetchB()
}

// Paralel — hızlı
coroutineScope {
    val a = async { api.fetchA() }
    val b = async { api.fetchB() }
    println("${a.await()} ${b.await()}")
}

Hata 3: Kritik işlemler için NonCancellable'ı unutma

withContext sırasında bir coroutine iptal edilirse, Dispatchers.IO üzerindeki blok da kesintiye uğrar. Ne pahasına olursa olsun tamamlanması gereken işlemler için (veritabanı yazma, analitik gönderme), withContext'i NonCancellable ile birleştirin.

Hata 4: IO bloğu içinde UI durumunu güncelleme

withContext(Dispatchers.IO) içinde asla View bileşenlerini güncellemeyin. withContext, tüm blok tamamlanana kadar Main'e dönmez. UI güncellemelerini withContext'in kapanış parantezinden sonra yerleştirin — o zaman coroutine zaten ana iş parçacığında olacaktır.

Sıkça sorulan sorular

withContext, runBlocking'den nasıl farklıdır?

withContext, iş parçacığını bloke etmeyen, mevcut bir coroutine içinde bağlamı değiştiren bir askıya alma fonksiyonudur. runBlocking, coroutine'ler ile normal kod arasında bir köprüdür ve tamamlanana kadar mevcut iş parçacığını bloke eder. withContext UI iş parçacığı için güvenlidir, runBlocking değildir.

withContext, suspend olmadan kullanılabilir mi?

Hayır, withContext bir suspend fonksiyonudur, bu nedenle yalnızca başka bir suspend fonksiyonundan veya bir coroutine'den (launch/async) çağrılabilir. Normal bir fonksiyondan withContext çağrılamaz — bunun için runBlocking veya CoroutineScope gerekir.

withContext'e aynı dağıtıcıyı iletirsem ne olur?

Kotlin hızlı yolu (fast-path) etkinleştirir — blok, aynı iş parçacığında değişiklik olmadan eşzamanlı olarak yürütülür. Ek yük 0,1 µs'den azdır. Bu bir hata değildir, ancak böyle bir çağrı gereksizdir — kodu withContext olmadan yürütmek daha iyidir.

withContext istisnalarla nasıl çalışır?

withContext içindeki istisnalar, normal koddaki gibi yayılır — try-catch aracılığıyla. Blok bir istisna fırlatırsa, üst coroutine'e yayılır ve işlenmezse onu iptal eder. withContext içinde veya etrafında try-catch kullanın.

withContext yeni bir coroutine oluşturur mu?

Hayır, withContext yeni bir coroutine oluşturmaz. Mevcut coroutine'i kullanır ancak bağlamını geçici olarak değiştirir. Bu, onu alt coroutine'ler oluşturan launch ve async'den ayırır. Bu davranış, kotlinx.coroutines kaynak kodu tarafından onaylanmıştır.

Özet

  • withContext — mevcut bir coroutine içinde CoroutineContext'i değiştirmek ve orijinal bağlama otomatik olarak dönmek için bir suspend fonksiyonu
  • Dispatchers.IO — withContext içinde ağ istekleri ve disk işlemleri için ana dağıtıcı
  • Hızlı yol (Fast-path) — withContext'in aynı dağıtıcı ile ek yük olmadan eşzamanlı olarak yürütüldüğü bir Kotlin optimizasyonu
  • Paralel görevler async/await gerektirir, withContext değil — withContext kodu sırayla yürütür
  • NonCancellable — coroutine iptal edildiğinde kesintiye uğramaması gereken kritik işlemler için bir bayrak
  • Repository katmanı — Google yönergelerine göre Android mimarisinde withContext için önerilen yer
  • Continuation — Kotlin bayt kodu düzeyinde withContext'te bağlam değiştirmenin temel mekanizması

Anahtar teslim bir mobil uygulama geliştireceğiz

IT Sectr, 2017'den beri girişimler ve işletmeler için iOS ve Android uygulamaları oluşturmaktadır. Size danışmanlık yapacak ve en iyi çözümü önereceğiz.

Projeyi tartış

Ayrıca okuyun