Engellemeyen API'ler, işin yapılmasını ister ve ardından istenen işlem tamamlanmadan önce diğer işleri yapabilmesi için kontrolü çağıran iş parçacığına geri verir. Bu API'ler, istenen çalışmanın devam ediyor olabileceği veya çalışmanın devam edebilmesi için G/Ç ya da IPC'nin tamamlanmasının, çok çekişmeli sistem kaynaklarının kullanılabilirliğinin ya da kullanıcı girişinin beklenmesi gerekebileceği durumlarda faydalıdır. Özellikle iyi tasarlanmış API'ler, devam eden işlemi iptal etmenin ve orijinal arayan adına çalışmayı durdurmanın bir yolunu sunar. Böylece, işlem artık gerekli olmadığında sistemin sağlığı ve pil ömrü korunur.
Eşzamansız API'ler, engellemeyen davranış elde etmenin bir yoludur. Asenkron API'ler, işlemin tamamlandığı veya işlem devam ederken diğer etkinliklerin gerçekleştiği durumlarda bildirilen bir devamlılık ya da geri çağırma biçimini kabul eder.
Asenkron API yazmanın iki temel nedeni vardır:
- N. işlemin N-1. işlem tamamlanmadan önce başlatılması gereken, birden fazla işlemin eşzamanlı olarak yürütülmesi.
- Bir işlem tamamlanana kadar arama iş parçacığının engellenmesini önleme.
Kotlin, yapılandırılmış eşzamanlılığı güçlü bir şekilde destekler. Bu, askıya alma işlevleri üzerine kurulu bir dizi ilke ve API'dir. Bu işlevler, kodun eşzamanlı ve eşzamansız yürütülmesini iş parçacığı engelleme davranışından ayırır. Askıya alma işlevleri engellemeyen ve senkronize işlevlerdir.
İşlevleri askıya alma:
- Bunların çağrı iş parçacığını engellemeyin. Bunun yerine, başka yerlerde yürütülen işlemlerin sonuçları beklenirken uygulama ayrıntısı olarak yürütme iş parçacıklarını bırakın.
- Eşzamanlı olarak yürütülür ve engellemeyen bir API'nin çağrısının, API çağrısı tarafından başlatılan engellemeyen çalışmayla eşzamanlı olarak yürütülmeye devam etmesini gerektirmez.
Bu sayfada, geliştiricilerin engellemeyen ve eşzamansız API'lerle çalışırken güvenle sahip olabileceği minimum temel beklentiler ayrıntılı olarak açıklanmaktadır. Ardından, Android platformunda veya Jetpack kitaplıklarında Kotlin ya da Java dillerinde bu beklentileri karşılayan API'ler oluşturmaya yönelik bir dizi tarif verilmektedir. Şüpheniz varsa geliştiricilerin beklentilerini yeni API yüzeylerinin şartları olarak değerlendirin.
Eş zamansız API'lerle ilgili geliştirici beklentileri
Aksi belirtilmediği sürece aşağıdaki beklentiler, askıya alınmayan API'ler açısından yazılmıştır.
Geri çağırmaları kabul eden API'ler genellikle eşzamansızdır.
Bir API, yalnızca yerinde çağrılmak üzere belgelenmemiş bir geri çağırmayı kabul ediyorsa (yani yalnızca API çağrısı döndürülmeden önce çağıran iş parçacığı tarafından çağrılıyorsa) API'nin eşzamansız olduğu varsayılır ve bu API, aşağıdaki bölümlerde belgelenen diğer tüm beklentileri karşılamalıdır.
Yalnızca yerinde çağrılan bir geri çağırma örneği, döndürmeden önce bir koleksiyondaki her öğede bir eşleyici veya yüklem çağıran yüksek sıralı bir harita ya da filtre işlevidir.
Eşzamansız API'ler mümkün olduğunca hızlı yanıt vermelidir.
Geliştiriciler, eşzamansız API'lerin engellemeyen olmasını ve işlem isteği başlatıldıktan sonra hızlı bir şekilde yanıt vermesini bekler. Eşzamansız bir API'yi istediğiniz zaman çağırmak her zaman güvenli olmalı ve eşzamansız bir API'yi çağırmak asla takılan karelere veya ANR'ye neden olmamalıdır.
Birçok işlem ve yaşam döngüsü sinyali, platform veya kitaplıklar tarafından isteğe bağlı olarak tetiklenebilir. Geliştiricinin, kodu için olası tüm çağrı siteleri hakkında genel bilgiye sahip olmasını beklemek sürdürülebilir değildir. Örneğin, uygulama içeriğinin mevcut alanı (ör. RecyclerView) dolduracak şekilde doldurulması gerektiğinde View ölçümü ve düzenine yanıt olarak eşzamanlı bir işlemde Fragment, FragmentManager öğesine eklenebilir. Bu parçanın onStart yaşam döngüsü geri çağırma yöntemine yanıt veren bir LifecycleObserver, burada tek seferlik başlatma işlemlerini makul bir şekilde gerçekleştirebilir. Bu da, animasyon karesinin sarsıntı olmadan üretilmesi için kritik bir kod yolunda olabilir. Geliştirici, bu tür yaşam döngüsü geri çağırma yöntemlerine yanıt olarak herhangi bir eş zamansız API'nin çağrılmasının, karelerin takılmasına neden olmayacağından her zaman emin olmalıdır.
Bu, döndürmeden önce bir eş zamansız API tarafından gerçekleştirilen çalışmanın çok hafif olması gerektiği anlamına gelir. En fazla, isteğin ve ilişkili geri çağırmanın kaydını oluşturup çalışmayı gerçekleştiren yürütme motoruna kaydetme işlemi yapılabilir. Asenkron bir işlem için kaydolmak IPC gerektiriyorsa API'nin uygulaması, bu geliştirici beklentisini karşılamak için gereken tüm önlemleri almalıdır. Aşağıdakilerden biri veya daha fazlası bu amaçlar arasında yer alabilir:
- Temel IPC'yi tek yönlü bağlayıcı çağrısı olarak uygulama
- Kayıt işleminin tamamlanması için çok çekişmeli bir kilidin alınmasını gerektirmeyen sistem sunucusuna iki yönlü bağlayıcı çağrı yapma
- IPC üzerinden engelleme kaydı gerçekleştirmek için isteği uygulama sürecindeki bir çalışan iş parçacığına gönderme
Eşzamansız API'ler boş değer döndürmeli ve yalnızca geçersiz bağımsız değişkenler için hata vermelidir.
Eş zamansız API'ler, istenen işlemin tüm sonuçlarını sağlanan geri çağırmaya bildirmelidir. Bu, geliştiricinin başarı ve hata işleme için tek bir kod yolu uygulamasına olanak tanır.
Asenkron API'ler, bağımsız değişkenlerin boş olup olmadığını kontrol edip NullPointerException istisnası oluşturabilir veya sağlanan bağımsız değişkenlerin geçerli bir aralıkta olup olmadığını kontrol edip IllegalArgumentException istisnası oluşturabilir. Örneğin, float ile 1f aralığında bir 0 kabul eden bir işlev, parametrenin bu aralıkta olup olmadığını kontrol edebilir ve aralığın dışında ise IllegalArgumentException değerini döndürebilir. Alternatif olarak, kısa bir String, yalnızca alfanümerik karakterler gibi geçerli bir biçime uygunluk açısından kontrol edilebilir. (Sistem sunucusunun uygulama sürecine hiçbir zaman güvenmemesi gerektiğini unutmayın. Herhangi bir sisteme ait hizmet, bu kontrolleri sisteme ait hizmetin kendisinde de tekrarlamalıdır.)
Diğer tüm hatalar, sağlanan geri arama işlevine bildirilmelidir. Bu, aşağıdakileri kapsar ancak bunlarla sınırlı değildir:
- İstenen işlemde terminal hata
- İşlemi tamamlamak için gereken yetki veya izinlerin eksik olmasıyla ilgili güvenlik istisnaları
- İşlemi gerçekleştirme kotası aşıldı
- Uygulama işlemi, işlemi gerçekleştirmek için yeterince "ön planda" değil
- Gerekli donanımın bağlantısı kesildi
- Ağ hataları
- Engelleme
- Bağlayıcı ölüm veya kullanılamayan uzak işlem
Eşzamansız API'ler bir iptal mekanizması sağlamalıdır
Asenkron API'ler, çalışan bir işleme arayanın artık sonuçla ilgilenmediğini belirtmenin bir yolunu sağlamalıdır. Bu iptal işlemi iki şeyi belirtmelidir:
Arayan tarafından sağlanan geri aramalarla ilgili kesin referanslar yayınlanmalıdır.
Eş zamansız API'lere sağlanan geri çağırmalar, büyük nesne grafiklerine sabit referanslar içerebilir ve bu geri çağırmaya sabit referans içeren devam eden çalışmalar, bu nesne grafiklerinin atık toplama işlemine alınmasını engelleyebilir. Bu geri çağırma referansları iptal üzerine serbest bırakıldığında, bu nesne grafikleri, işin tamamlanmasına izin verilmesi durumuna kıyasla çok daha erken bir zamanda çöp toplama işlemine uygun hale gelebilir.
Arayan için iş yapan yürütme motoru, bu işi durdurabilir.
Asenkron API çağrılarıyla başlatılan işlemler, güç tüketimi veya diğer sistem kaynakları açısından yüksek maliyetli olabilir. Arayanların bu çalışmaya artık gerek kalmadığını bildirmesine olanak tanıyan API'ler, bu çalışmanın daha fazla sistem kaynağı tüketmeden durdurulmasına izin verir.
Önbelleğe alınan veya dondurulmuş uygulamalarla ilgili dikkat edilmesi gereken noktalar
Geri çağırmaların bir sistem sürecinde başladığı ve uygulamalara teslim edildiği asenkron API'ler tasarlarken aşağıdakileri göz önünde bulundurun:
- Süreçler ve uygulama yaşam döngüsü: Alıcı uygulama süreci önbelleğe alınmış durumda olabilir.
- Önbelleğe alınmış uygulamaları dondurma: Alıcı uygulama işlemi dondurulmuş olabilir.
Bir uygulama süreci önbelleğe alınmış duruma girdiğinde, etkinlikler ve hizmetler gibi kullanıcı tarafından görülebilen bileşenleri etkin olarak barındırmadığı anlamına gelir. Uygulama, tekrar kullanıcı tarafından görünür hale gelmesi ihtimaline karşı bellekte tutulur ancak bu süre zarfında herhangi bir işlem yapmamalıdır. Çoğu durumda, uygulama önbelleğe alınmış duruma girdiğinde uygulama geri çağırmalarının gönderilmesini duraklatmanız ve uygulama önbelleğe alınmış durumdan çıktığında devam ettirmeniz gerekir. Böylece, önbelleğe alınmış uygulama işlemlerinde işleme neden olmazsınız.
Önbelleğe alınmış bir uygulama da dondurulabilir. Dondurulan uygulamalar CPU süresi almaz ve hiçbir işlem yapamaz. Bu uygulamanın kayıtlı geri aramalarına yapılan tüm aramalar arabelleğe alınır ve uygulama dondurulması kaldırıldığında teslim edilir.
Uygulama geri çağırmalarına yönelik arabelleğe alınmış işlemler, uygulama dondurulması kaldırılıp bu işlemleri gerçekleştirdiğinde eski olabilir. Arabellek sınırlıdır ve taşarsa alıcı uygulamanın kilitlenmesine neden olur. Uygulamaların eski etkinliklerle aşırı yüklenmesini veya arabelleklerinin taşmasını önlemek için işlemleri dondurulmuşken uygulama geri çağırmalarını göndermeyin.
İnceleniyor:
- Uygulamanın işlemi önbelleğe alınırken uygulama geri aramalarının gönderilmesini duraklatmayı düşünebilirsiniz.
- Uygulamanın işlemi dondurulmuşken uygulama geri çağırmalarının gönderilmesini DURAKLATMANIZ GEREKİR.
Durum izleme
Uygulamaların ne zaman önbelleğe alınmış duruma girdiğini veya bu durumdan çıktığını izlemek için:
mActivityManager.addOnUidImportanceListener(
new UidImportanceListener() { ... },
IMPORTANCE_CACHED);
Uygulamaların ne zaman dondurulduğunu veya çözüldüğünü takip etmek için:
IBinder binder = <...>;
binder.addFrozenStateChangeCallback(executor, callback);
Uygulama geri çağırmalarını göndermeye devam etme stratejileri
Uygulama önbelleğe alınmış duruma veya dondurulmuş duruma girdiğinde uygulama geri çağırmalarının gönderilmesini duraklatıp duraklatmadığınıza bakılmaksızın, uygulama ilgili durumdan çıktığında uygulama geri çağırmalarını göndermeye devam etmeniz gerekir. Bu işlem, uygulama geri çağırmasını kaydını silene veya uygulama işlemi sonlanana kadar devam eder.
Örneğin:
IBinder binder = <...>;
bool shouldSendCallbacks = true;
binder.addFrozenStateChangeCallback(executor, (who, state) -> {
if (state == IBinder.FrozenStateChangeCallback.STATE_FROZEN) {
shouldSendCallbacks = false;
} else if (state == IBinder.FrozenStateChangeCallback.STATE_UNFROZEN) {
shouldSendCallbacks = true;
}
});
Alternatif olarak, hedef işlem dondurulduğunda geri çağırmaların teslim edilmemesini sağlayan RemoteCallbackList kullanabilirsiniz.
Örneğin:
RemoteCallbackList<IInterface> rc =
new RemoteCallbackList.Builder<IInterface>(
RemoteCallbackList.FROZEN_CALLEE_POLICY_DROP)
.setExecutor(executor)
.build();
rc.register(callback);
rc.broadcast((callback) -> callback.foo(bar));
callback.foo() yalnızca işlem dondurulmamışsa çağrılır.
Uygulamalar, geri çağırmaları kullanarak aldıkları güncellemeleri genellikle son durumun anlık görüntüsü olarak kaydeder. Uygulamaların kalan pil yüzdesini izlemesini sağlayan varsayıma dayalı bir API düşünün:
interface BatteryListener {
void onBatteryPercentageChanged(int newPercentage);
}
Bir uygulama dondurulduğunda birden fazla durum değişikliği etkinliğinin gerçekleştiği senaryoyu ele alalım. Uygulamanın dondurulması kaldırıldığında, uygulamaya yalnızca en son durumu iletmeniz ve diğer eski durum değişikliklerini bırakmanız gerekir. Bu teslimat, uygulama dondurulması kaldırıldığında hemen gerçekleşmelidir. Böylece uygulama "güncel" hale gelebilir. Bu işlem şu şekilde yapılabilir:
RemoteCallbackList<IInterface> rc =
new RemoteCallbackList.Builder<IInterface>(
RemoteCallbackList.FROZEN_CALLEE_POLICY_ENQUEUE_MOST_RECENT)
.setExecutor(executor)
.build();
rc.register(callback);
rc.broadcast((callback) -> callback.onBatteryPercentageChanged(value));
Bazı durumlarda, uygulamaya son teslim edilen değeri izleyebilirsiniz. Böylece uygulama, dondurulması kaldırıldıktan sonra aynı değerden haberdar edilmek zorunda kalmaz.
Durum, daha karmaşık veriler olarak ifade edilebilir. Uygulamaların ağ arayüzleri hakkında bildirim alması için varsayıma dayalı bir API düşünün:
interface NetworkListener {
void onAvailable(Network network);
void onLost(Network network);
void onChanged(Network network);
}
Bir uygulamaya yönelik bildirimleri duraklatırken uygulamanın en son gördüğü ağları ve durumları hatırlamanız gerekir. Devam ettirildikten sonra, sırasıyla kaybolan eski ağları, kullanılabilir hale gelen yeni ağları ve durumu değişen mevcut ağları uygulamaya bildirmeniz önerilir.
Geri çağırmalar duraklatılırken kullanılabilir hale gelen ve ardından kaybedilen ağlar hakkında uygulamaya bildirim göndermeyin. Uygulamalar, donduruldukları sırada gerçekleşen etkinliklerin tam dökümünü almamalıdır ve API belgelerinde, etkinlik akışlarının açık yaşam döngüsü durumları dışında kesintisiz olarak sunulacağı vaat edilmemelidir. Bu örnekte, uygulamanın ağ kullanılabilirliğini sürekli olarak izlemesi gerekiyorsa uygulamanın, önbelleğe alınmasını veya dondurulmasını engelleyen bir yaşam döngüsü durumunda kalması gerekir.
İnceleme sırasında, bildirimler duraklatıldıktan sonra ve devam ettirilmeden önce gerçekleşen etkinlikleri birleştirip kayıtlı uygulama geri çağırmalarına en son durumu kısa ve öz bir şekilde iletmeniz gerekir.
Geliştirici belgeleriyle ilgili dikkat edilmesi gereken noktalar
Asenkron etkinliklerin teslimatı, gönderenin önceki bölümde gösterildiği gibi teslimatı bir süre duraklatması veya alıcı uygulamanın etkinliği zamanında işlemek için yeterli cihaz kaynağı almaması nedeniyle gecikebilir.
Geliştiricilerin, uygulamalarına bir etkinlik bildirimi gönderilmesi ile etkinliğin gerçekten gerçekleşmesi arasındaki süre hakkında varsayımlarda bulunmasını önleyin.
API'lerin askıya alınmasıyla ilgili geliştirici beklentileri
Kotlin'in yapılandırılmış eşzamanlılığına aşina olan geliştiriciler, askıya alma işlevine sahip tüm API'lerden aşağıdaki davranışları bekler:
Askıya alma işlevleri, geri dönmeden veya hata vermeden önce ilişkili tüm çalışmaları tamamlamalıdır.
Engellemeyen işlemlerin sonuçları normal işlev dönüş değerleri olarak döndürülür ve hatalar istisnalar oluşturularak bildirilir. (Bu genellikle geri çağırma parametrelerinin gereksiz olduğu anlamına gelir.)
Askıya alma işlevleri, geri çağırma parametrelerini yalnızca yerinde çağırmalıdır.
Askıya alma işlevleri, döndürmeden önce her zaman ilişkili tüm çalışmaları tamamlamalıdır. Bu nedenle, sağlanan bir geri çağırma işlevini veya başka bir işlev parametresini asla çağırmamalı ya da askıya alma işlevi döndükten sonra buna referans tutmamalıdır.
Geri çağırma parametrelerini kabul eden askıya alma işlevleri, aksi belirtilmediği sürece bağlamı korumalıdır.
Bir askıya alma işlevinde işlev çağırmak, işlevin arayanın CoroutineContext içinde çalışmasına neden olur. Askıya alma işlevleri, döndürme veya hata oluşturma işleminden önce ilişkili tüm işleri tamamlamalı ve geri çağırma parametrelerini yalnızca yerinde çağırmalıdır. Bu nedenle, bu tür geri çağırmaların, ilişkili göndericisi kullanılarak çağıran CoroutineContext üzerinde de çalıştırılması beklenir. API'nin amacı, çağırma işlevinin dışında bir geri çağırma işlevi çalıştırmaksa bu davranış açıkça belgelenmelidir.CoroutineContext
Askıya alma işlevleri, kotlinx.coroutines iş iptalini desteklemelidir.
Sunulan tüm askıya alma işlevleri, kotlinx.coroutines tarafından tanımlandığı şekilde iş iptaliyle uyumlu olmalıdır. Devam eden bir işlemin çağırma işi iptal edilirse, arayanın temizlik yapıp en kısa sürede devam edebilmesi için işlev en kısa sürede CancellationException ile devam etmelidir. Bu işlem, suspendCancellableCoroutine ve kotlinx.coroutines tarafından sunulan diğer askıya alma API'leri tarafından otomatik olarak gerçekleştirilir. Kitaplık uygulamaları genellikle suspendCoroutine öğesini doğrudan kullanmamalıdır. Çünkü bu öğe, iptal davranışını varsayılan olarak desteklemez.
Arka planda (ana olmayan veya kullanıcı arayüzü iş parçacığı) engelleme işi yapan işlevler, kullanılan göndericiyi yapılandırma yöntemi sağlamalıdır.
İş parçacıklarını değiştirmek için engelleme işlevinin tamamen askıya alınması önerilmez.
Bir askıya alma işlevinin çağrılması, geliştiricinin bu işi gerçekleştirmek için kendi iş parçacığını veya iş parçacığı havuzunu sağlamasına izin verilmeden ek iş parçacıklarının oluşturulmasına neden olmamalıdır. Örneğin, bir oluşturucu, sınıfın yöntemleri için arka plan çalışması yapmak üzere kullanılan bir CoroutineContext kabul edebilir.
Yalnızca engelleme işlemini gerçekleştirmek için söz konusu dağıtıcıya geçmek üzere isteğe bağlı bir CoroutineContext veya Dispatcher parametresini kabul eden işlevler askıya alınmalıdır. Bunun yerine, temel engelleme işlevi kullanıma sunulmalı ve işi seçilen bir dağıtıcıya yönlendirmek için arayan geliştiricilerin kendi withContext çağrılarını kullanmaları önerilmelidir.
Coroutine başlatan sınıflar
Coroutine'leri başlatan sınıflar, bu başlatma işlemlerini gerçekleştirmek için CoroutineScope içermelidir. Yapılandırılmış eşzamanlılık ilkelerine uymak, bu kapsamı elde etmek ve yönetmek için aşağıdaki yapısal kalıpları kullanmayı gerektirir.
Eşzamanlı görevleri başka bir kapsama başlatan bir sınıf yazmadan önce alternatif kalıpları göz önünde bulundurun:
class MyClass {
private val requests = Channel<MyRequest>(Channel.UNLIMITED)
suspend fun handleRequests() {
coroutineScope {
for (request in requests) {
// Allow requests to be processed concurrently;
// alternatively, omit the [launch] and outer [coroutineScope]
// to process requests serially
launch {
processRequest(request)
}
}
}
}
fun submitRequest(request: MyRequest) {
requests.trySend(request).getOrThrow()
}
}
Eşzamanlı çalışma yapmak için suspend fun'yı kullanıma sunmak, arayanın işlemi kendi bağlamında çağırmasına olanak tanır ve MyClass'nın CoroutineScope'yi yönetmesine gerek kalmaz. İsteklerin işlenmesini serileştirmek daha kolay hale gelir ve durum, genellikle ek senkronizasyon gerektirecek sınıf özellikleri yerine handleRequests'nın yerel değişkenleri olarak var olabilir.
Coroutine'ları yöneten sınıflar, kapatma ve iptal etme yöntemlerini kullanıma sunmalıdır
Uygulama ayrıntıları olarak eşzamanlı yordamları başlatan sınıflar, devam eden eşzamanlı görevleri temiz bir şekilde kapatmanın bir yolunu sunmalıdır. Böylece, kontrolsüz eşzamanlı işlerin üst kapsamda sızması önlenir. Bu işlem genellikle sağlanan bir CoroutineContext öğesinin Job öğesini oluşturma şeklinde gerçekleşir:
private val myJob = Job(parent = `CoroutineContext`[Job])
private val myScope = CoroutineScope(`CoroutineContext` + myJob)
fun cancel() {
myJob.cancel()
}
Ayrıca, kullanıcı kodunun nesne tarafından gerçekleştirilen eşzamanlı işlemlerin tamamlanmasını beklemesine izin vermek için bir join() yöntemi de sağlanabilir.
(Bu, bir işlemi iptal ederek gerçekleştirilen temizleme çalışmalarını içerebilir.)
suspend fun join() {
myJob.join()
}
Terminal işlemi adlandırma
Bir nesnenin sahip olduğu ve hâlâ devam eden eşzamanlı görevleri düzgün bir şekilde kapatmak için kullanılan yöntemlerin adı, kapatma işleminin nasıl gerçekleştiğiyle ilgili davranış sözleşmesini yansıtmalıdır:
Devam eden işlemlerin tamamlanabileceği ancak close() çağrısı döndükten sonra yeni işlemlerin başlatılamayacağı durumlarda close() kullanın.
Devam eden işlemler tamamlanmadan önce iptal edilebileceği durumlarda cancel() kullanın.
cancel() çağrısı döndükten sonra yeni işlemler başlatılamaz.
Sınıf oluşturucular, CoroutineScope değil CoroutineContext kabul eder
Nesnelerin doğrudan sağlanan bir üst kapsamda başlatılması yasaklandığında, CoroutineScope öğesinin oluşturucu parametresi olarak uygunluğu bozulur:
// Don't do this
class MyClass(scope: CoroutineScope) {
private val myJob = Job(parent = scope.`CoroutineContext`[Job])
private val myScope = CoroutineScope(scope.`CoroutineContext` + myJob)
// ... the [scope] constructor parameter is never used again
}
CoroutineScope, bazı kullanım alanlarında yalnızca oluşturucu parametresi olarak geçmek için oluşturulabilen, gereksiz ve yanıltıcı bir sarmalayıcı haline gelir ve daha sonra atılır:
// Don't do this; just pass the context
val myObject = MyClass(CoroutineScope(parentScope.`CoroutineContext` + Dispatchers.IO))
CoroutineContext parametreleri varsayılan olarak EmptyCoroutineContext değerine ayarlanır.
Bir API yüzeyinde isteğe bağlı bir CoroutineContext parametresi göründüğünde varsayılan değer Empty`CoroutineContext` sentinel olmalıdır. Bu, arayan tarafından sağlanan Empty`CoroutineContext` değerinin varsayılanı kabul etmekle aynı şekilde ele alınması nedeniyle API davranışlarının daha iyi oluşturulmasına olanak tanır:
class MyOuterClass(
`CoroutineContext`: `CoroutineContext` = Empty`CoroutineContext`
) {
private val innerObject = MyInnerClass(`CoroutineContext`)
// ...
}
class MyInnerClass(
`CoroutineContext`: `CoroutineContext` = Empty`CoroutineContext`
) {
private val job = Job(parent = `CoroutineContext`[Job])
private val scope = CoroutineScope(`CoroutineContext` + job)
// ...
}