CoroutineScope — nedir, yaşam döngüsü ve coroutine'lerde çalışma

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

CoroutineScope, bir coroutine'in yaşam döngüsünü tanımlayan ve yeni coroutine'ler başlatmak için bağlam sağlayan bir Kotlin arayüzüdür. Kotlin belgelerine, 2025 göre, her CoroutineScope örneği bir CoroutineContext içerir ve içinde başlatılan tüm coroutine'leri yönetir. Scope iptal edildiğinde (cancel), tüm alt coroutine'ler otomatik olarak iptal edilir ve bellek sızıntıları önlenir.

Önemli noktalar

  • CoroutineScope — tek bir CoroutineContext alanına sahip arayüz, coroutine'lerin yaşam döngüsünü tanımlar
  • Job — iptalden sorumlu bağlam öğesi: scope'u iptal etmek tüm alt coroutine'leri iptal eder
  • Yapısal eşzamanlılık — alt coroutine'lerin üst scope'a bağlı olduğu ilke
  • GlobalScope — bellek sızıntısı riski nedeniyle önerilmeyen uygulama genelindeki scope
  • supervisorScope — bir alt coroutine'i iptal etmenin diğerlerini iptal etmediği özel scope

Kotlin'de CoroutineScope nedir?

CoroutineScope, kotlinx.coroutines kütüphanesinden coroutine'ler için bir kapsayıcı görevi gören temel bir arayüzdür. Coroutine yaşam döngüsünün sınırlarını tanımlar: scope tamamlandığında, içindeki tüm coroutine'ler otomatik olarak iptal edilir.

kotlin
public interface CoroutineScope {
    public val coroutineContext: CoroutineContext
}

Arayüz yalnızca bir alan içerir — coroutineContext. Scope, bu alan aracılığıyla içinde başlatılan tüm coroutine'lere bir dağıtıcı (Dispatcher), bir iş (Job), bir istisna işleyici ve diğer bağlam öğelerini sağlar.

kotlinx.coroutines kütüphanesindeki rolü

Tüm coroutine başlatma işlevleri — launch, async, runBlocking — CoroutineScope üzerindeki genişletme işlevleridir. Bu, yalnızca bir scope nesnesi mevcut olduğunda çağrılabilecekleri anlamına gelir. Bu tasarım, her coroutine'in açıkça tanımlanmış bir üst öğeye ve yaşam döngüsüne sahip olmasını sağlar.

CoroutineScope nerelerde kullanılır

Android'de, her mimari bileşenin kendi scope'u vardır: ViewModel için viewModelScope, Activity/Fragment için lifecycleScope. Sunucu uygulamalarında, scope bir HTTP isteğine veya veritabanı bağlantı havuzuna bağlanabilir.

CoroutineScope nasıl çalışır: Job ve yapısal eşzamanlılık

CoroutineScope'un iç çalışmasını anlamak, Job kavramına ve yapısal eşzamanlılık ilkesine aşinalık gerektirir.

Job — coroutine'in görevi

Her coroutine başlatıldığında bir Job nesnesi (veya async için Deferred) döndürür. Job, sınırlı bir yaşam döngüsüne sahip bir görevi temsil eder: New, Active, Completing, Completed, Cancelling, Cancelled. Job nesneleri bir ağaç yapısı oluşturur:

  • Üst Job — coroutine'in başlatıldığı scope
  • Alt Job — launch/async aracılığıyla başlatılan her coroutine
  • Üstün iptali → tüm alt öğelerin iptali
  • Alt öğede istisna → üstün iptali (supervisorScope hariç)

Yapısal eşzamanlılık ilkesi

Yapısal eşzamanlılık, Kotlin Coroutines'in temel bir mimari ilkesidir ve coroutine'in yaşam döngüsünün scope'unun yaşam döngüsüne bağlı olduğu anlamına gelir. Bu, coroutine'in scope tamamlandıktan sonra da yaşamaya devam ettiği “ateşle ve unut” modelinin tersidir. Yapısal eşzamanlılığın avantajları:

  • Tahmin edilebilir yaşam döngüsü — scope tamamlandığında tüm coroutine'ler durdurulur
  • Otomatik hata işleme — herhangi bir alt coroutine'deki istisna scope'a yayılır
  • Bellek sızıntısı yok — scope tamamlandıktan sonra hiçbir coroutine çalışmaya devam etmez
  • Net hiyerarşi — kod, paralel işlemlerin mantıksal yapısını yansıtır

CoroutineScope'un yaşam döngüsü

scope.cancel() çağrıldığında, scope'un Job'ı Cancelled durumuna geçer ve bu, tüm alt Job'ları yinelemeli olarak iptal eder. İptalden sonra, scope yalnızca yeni bir CoroutineScope örneği oluşturularak yeniden kullanılabilir.

CoroutineScope oluşturma ve yapılandırma

CoroutineScope'u bir fabrika işlevi aracılığıyla veya sınıfınızda arayüzü uygulayarak oluşturabilirsiniz. Her iki yaklaşımı da inceleyelim.

Fabrika işlevi CoroutineScope()

kotlin
val scope = CoroutineScope(Dispatchers.Default + SupervisorJob())

scope.launch {
    println("Running on ${Thread.currentThread().name}")
}

Fabrika işlevi bir CoroutineContext alır ve belirtilen bağlamla bir scope oluşturur. Örnek, CPU yoğun görevler için Dispatchers.Default ve alt coroutine'ler arasındaki istisnaları yalıtan SupervisorJob'ı kullanır.

Arayüzü kompozisyon yoluyla uygulama

kotlin
class MyRepository {
    private val scope = CoroutineScope(Dispatchers.IO + Job())

    suspend fun fetchData(): Data = scope.async {
        api.getData()
    }.await()

    fun cleanup() {
        scope.cancel()
    }
}

Scope'u bir sınıf alanı olarak saklar ve iptal etmek için manuel olarak cleanup çağırırız. Bu yaklaşım, yönetilen yaşam döngüsüne sahip bileşenler — örneğin, depo veya yöneticiler — için uygundur.

Yetkilendirme yoluyla uygulama

Kotlin, by anahtar sözcüğü aracılığıyla CoroutineScope uygulamasını devretmeye izin verir:

kotlin
class DataLoader : CoroutineScope by CoroutineScope(Dispatchers.IO) {
    fun load() {
        launch {
            // coroutine runs in DataLoader scope
        }
    }
}

Bu yaklaşım, sınıfın kendisi bir scope olduğunda ve coroutine başlatma yöntemleri sağlamak istediğinde kullanışlıdır. Ancak dikkatli olun: sınıf, CoroutineScope'un tüm yöntemlerini (cancel dahil) devralır ve bu da kapsüllemeyi bozabilir.

GlobalScope vs özel CoroutineScope

GlobalScope, tüm uygulama için bir tekil CoroutineScope'tur. Üretim kodunda kullanımı resmi olarak önerilmez.

GlobalScope sorunları

  • Yapısal eşzamanlılık eksikliği — GlobalScope'taki coroutine'ler bileşen yaşam döngüsüne bağlı değildir
  • Bellek sızıntıları — Activity/Fragment kapatıldıktan sonra bir coroutine çalışmaya devam edebilir
  • Zor test — GlobalScope testlerde değiştirilemez
  • Kontrolsüz kaynak tüketimi — birçok coroutine beklenenden daha uzun süre çalışabilir

GlobalScope ne zaman haklıdır

JetBrains, GlobalScope'a yalnızca nadir senaryolarda izin verir: tüm Activity'ler kapatıldıktan sonra bile yaşaması gereken uygulama düzeyindeki arka plan işlemleri (örneğin, veri senkronizasyonu, analitik). Ancak bu durumlarda bile, CoroutineScope(SupervisorJob()) ile kendi scope'unuzu oluşturmanız tercih edilir.

Öneri

Açık yaşam döngüsü yönetimi ile her zaman özel bir CoroutineScope kullanın. Android'de bunlar viewModelScope ve lifecycleScope'tur. Sunucu uygulamalarında, her istek veya bağlantı havuzu için bir scope oluşturun.

coroutineScope vs supervisorScope: fark nedir

Her iki işlev de paralel görevler için geçici bir scope oluşturan askıya alma işlevleridir, ancak istisnalar karşısındaki davranışları temelde farklıdır.

ÖzellikcoroutineScopesupervisorScope
Hata durumunda davranışAlt coroutine'deki istisna diğerlerini iptal ederAlt coroutine'deki istisna diğerlerini iptal etmez
Hata yayılımıEvet, ilk istisna dışarıya yayılırEvet, ilk istisna dışarıya yayılır
Varsayılan JobJob() — alt öğeler üst öğeye bağlıSupervisorJob() — alt öğeler birbirine bağlı değil
Tipik kullanım durumuÇok adımlı atomik işlemBağımsız paralel görevler (UI yüklemeleri)

coroutineScope ne zaman seçilmeli

Birden çok paralel işlem tek bir atomik işlem oluşturduğunda coroutineScope kullanın. Örneğin, üç sunucudan veri yükleme: bir istek başarısız olursa, diğerleri anlamsızdır.

kotlin
suspend fun loadProductPage(): ProductPage = coroutineScope {
    val product = async { api.getProduct() }
    val reviews = async { api.getReviews() }
    ProductPage(product.await(), reviews.await())
}

getProduct veya getReviews bir istisna fırlatırsa — her iki coroutine de iptal edilir ve istisna çağıran koda yayılır.

supervisorScope ne zaman seçilmeli

Paralel işlemler birbirine bağlı olmadığında supervisorScope kullanın. Örneğin, birden çok bağımsız bölümde profil verilerini yükleme: öneriler bölümü başarısız olursa, profil başlığı ve arkadaş listesi görüntülenmelidir.

CoroutineScope ile çalışırken yaygın hatalar

Kotlin'de CoroutineScope kullanırken geliştiricilerin en yaygın hatalarına bakalım.

Hata 1: Scope'u iptal etmeyi unutmak

En yaygın coroutine sızıntısı senaryosu, bileşen sona erdiğinde cancel çağırmadan scope oluşturmaktır. Scope iptal edilmezse, coroutine'ler çalışmaya devam eder ve nesnelere referansları tutar. Android'de, otomatik olarak iptal edilen viewModelScope veya lifecycleScope kullanın.

Hata 2: Activity veya Fragment'te GlobalScope kullanmak

GlobalScope, Android bileşen yaşam döngüsünü yok sayar. Bir Activity kapatıldıktan sonra GlobalScope'ta başlatılan bir coroutine çalışmaya devam eder ve UI'yi güncellemeye çalışır — bu da çökmeye neden olur. UI bileşenleri için her zaman lifecycleScope kullanın.

Hata 3: İptal edilmiş bir scope'u yeniden kullanmak

cancel() çağrıldıktan sonra scope yeniden kullanılamaz — içindeki tüm coroutine'ler zaten tamamlanmıştır. Fabrika işlevi aracılığıyla yeni bir CoroutineScope örneği oluşturun. Job() yeniden etkinleştirmeyi desteklemez.

Hata 4: CoroutineScope arayüzünün yanlış devredilmesi

by ile devredildiğinde, sınıf her yerden çağrılabilen genel bir cancel() yöntemi alır ve kapsüllemeyi bozar. Arayüzü devretmek yerine scope'u özel bir alan olarak saklayın.

Sık sorulan sorular

CoroutineScope ile CoroutineContext arasındaki fark nedir?

CoroutineScope, bir CoroutineContext'e sahip olan ve coroutine'lerin yaşam döngüsünden sorumlu olan bir arayüzdür. CoroutineContext, bir coroutine'in “nasıl” yürütüleceğini tanımlayan öğeler (dağıtıcı, job, hata işleyici) kümesidir. Bir fark: scope coroutine'leri oluştururken, bağlam onların davranışını kontrol eder.

SupervisorJob ile CoroutineScope oluşturulabilir mi?

Evet, bu standart bir kalıptır: CoroutineScope(Dispatchers.IO + SupervisorJob()). SupervisorJob, alt coroutine'lerden biri bir istisna fırlattığında diğerlerinin zincirleme iptalini önler. Bu, birindeki hatanın diğerlerini durdurmaması gereken bağımsız paralel görevler için kullanışlıdır.

Bir CoroutineScope kaç coroutine içerebilir?

Bir scope'taki coroutine sayısında sınır yoktur — yalnızca kullanılabilir bellek ve dağıtıcı ayarlarıyla sınırlıdırlar. Pratik sınır genellikle tek bir scope'ta binlerce etkin coroutine'dir. Ancak, çok sayıda coroutine mimari sorunlara işaret edebilir.

CoroutineScope ile kod nasıl test edilir?

Doğru yol, scope'u kurucu aracılığıyla sınıfa geçirmek veya kotlinx-coroutines-test'ten runBlockingTest / runTest kullanmaktır. Testlerde, scope'u TestCoroutineDispatcher ile değiştirebilir ve coroutine yürütmesini manuel olarak kontrol edebilirsiniz.

Bir coroutine'in kendi scope'u olabilir mi?

Hayır, scope bir coroutine için harici bir kapsayıcıdır. Coroutine'in kendisi bir scope değildir. Ancak, bir coroutine içinde coroutineScope veya supervisorScope aracılığıyla alt coroutine'leri paralel olarak başlatmak için yeni bir scope oluşturabilirsiniz.

Özet

  • CoroutineScope — coroutineContext alanına sahip arayüz, içinde başlatılan coroutine'lerin yaşam döngüsünü tanımlar
  • Yapısal eşzamanlılık — scope'u iptal etmek tüm alt coroutine'leri otomatik olarak iptal eder, bellek sızıntılarını önler
  • Job ve SupervisorJob — iki hata işleme modu: zincirleme iptal (Job) ve yalıtılmış hatalar (SupervisorJob)
  • GlobalScope — yaşam döngüsü bağlantısı eksikliği nedeniyle üretim için önerilmez
  • coroutineScope vs supervisorScope — atomik paralel işlemler vs bağımsız paralel görevler
  • viewModelScope ve lifecycleScope — Android için hazır scope'lar, bileşen sona erdiğinde otomatik olarak iptal edilir
  • Fabrika işlevi — CoroutineContext + açık cancel çağrısı yoluyla scope oluşturmanın tercih edilen yolu

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