Engellemeyen API'ler, çalışmanın yapılmasını ister ve ardından istenen işlemin tamamlanmasından önce başka çalışmalar 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/Ç'nin veya IPC'nin tamamlanmasını, yoğun şekilde paylaşılan sistem kaynaklarının kullanılabilir olmasını ya da kullanıcı girişini beklemeyi gerektirebileceği durumlarda kullanışlıdır. Özellikle iyi tasarlanmış API'ler, devam eden işlemi iptal etme ve orijinal arayanın adına çalışmanın durdurulmasını sağlayan bir yöntem sunar. Böylece, işleme artık ihtiyaç duyulmadığında sistem sağlığı ve pil ömrü korunur.
Eşzamansız API'ler, engellenmeyen davranış elde etmenin bir yoludur. Asynkron API'ler, işlem tamamlandığında veya işlem sırasındaki diğer etkinliklerde bildirim alan bir tür devam etme veya geri çağırma işlevi kabul eder.
Asenkron API yazmanın iki temel nedeni vardır:
- N. işlem, N-1. işlem tamamlanmadan önce başlatılması gereken birden fazla işlemin eşzamanlı olarak yürütülmesi.
- İşlem tamamlanana kadar bir çağrı iş parçacığının engellenmesini önleme.
Kotlin, kodun senkron ve asenkron yürütülmesini iş parçacığı engelleme davranışından ayıran askıya alma işlevlerine dayalı bir dizi ilke ve API olan yapılandırılmış eşzamanlılığı güçlü bir şekilde destekler. Askıya alma işlevleri engellemesiz ve eşzamanlı işlevlerdir.
İşlevleri askıya alma:
- Çağıran iş parçacıklarını engellemeyin. Bunun yerine, başka bir yerde yürütülen işlemlerin sonuçlarını beklerken yürütme iş parçacıklarını bir uygulama ayrıntısı olarak verin.
- Senkronize olarak yürütülür ve API çağrısını yapan kullanıcının, API çağrısı tarafından başlatılan engellenmeyen çalışmayla eşzamanlı olarak yürütmeye devam etmesini gerektirmez.
Bu sayfada, geliştiricilerin engellenmeyen ve asenkron API'lerle çalışırken güvenle karşılayabileceği minimum 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 sunulmaktadır. Emin değilseniz geliştirici beklentilerini yeni API yüzeyi için şartlar olarak değerlendirin.
Eşzamansız API'lerle ilgili geliştirici beklentileri
Aşağıdaki beklentiler, aksi belirtilmedikçe askıya alınmayan API'ler açısından yazılmıştır.
Geri çağırma işlevini kabul eden API'ler genellikle eşzamansızdır.
Bir API, yalnızca yerinde çağrılacağı belgelenmemiş bir geri çağırma işlevi kabul ederse (yani, yalnızca API çağrısı kendisi dönmeden önce çağıran iş parçacığı tarafından çağrılırsa) API'nin asenkron olduğu varsayılır ve API, aşağıdaki bölümlerde belirtilen diğer tüm beklentileri karşılamalıdır.
Yalnızca yerinde çağrılan bir geri çağırma işlevi örneği, döndürmeden önce bir koleksiyondaki her öğede bir eşleyici veya önerme çağıran daha yüksek düzey bir eşleme veya filtre işlevidir.
Eşzamansız API'ler mümkün olduğunca hızlı bir şekilde yanıt vermelidir.
Geliştiriciler, asenkron API'lerin engelleyici olmamasını ve işlem isteği başlatıldıktan sonra hızlı bir şekilde yanıt vermesini bekler. Eşzamansız API'yi herhangi bir zamanda çağırmak her zaman güvenli olmalıdır ve eşzamansız API'yi çağırmak hiçbir zaman sarsıntılı 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. Bu nedenle, geliştiricilerin kodları için tüm potansiyel çağrı siteleri hakkında genel bilgi sahibi olması sürdürülebilir değildir. Örneğin, uygulama içeriğinin mevcut alanı doldurmak için doldurulması gerektiğinde (ör. RecyclerView
) View
ölçümüne ve düzenine yanıt olarak Fragment
, senkronize bir işlemde FragmentManager
'a eklenebilir. Bu parçanın onStart
yaşam döngüsü geri çağırmasına yanıt veren bir LifecycleObserver
, burada makul bir şekilde tek seferlik başlatma işlemleri gerçekleştirebilir ve bu, sarsıntısız bir animasyon karesi oluşturmak için kritik bir kod yolunda olabilir. Geliştiriciler, bu tür yaşam döngüsü geri çağırmalarına yanıt olarak herhangi bir arayüz çağırmanın, sarsıntılı bir kare oluşturmayacağından her zaman emin olmalıdır.
Bu, döndürmeden önce bir asenkron API tarafından gerçekleştirilen çalışmanın çok hafif olması gerektiği anlamına gelir. Bu çalışma, isteğin ve ilişkili geri çağırma işlevinin kaydını oluşturup en fazla işi gerçekleştiren yürütme motoruna kaydeder. Asenkron bir işleme kaydolmak için IPC gerekiyorsa API'nin uygulanmasında, geliştirici beklentisini karşılamak için gerekli tüm önlemler alınmalıdır. Bu, aşağıdakilerden biri veya daha fazlasını içerebilir:
- Temel IPC'yi tek yönlü bağlayıcı çağrısı olarak uygulama
- Kayıt işleminin tamamlanması için çok fazla çekişmeli kilit alınması gerekmeyen sistem sunucusuna iki yönlü bir bağlayıcı çağrısı yapma
- IPC üzerinden engelleme kaydı gerçekleştirmek için isteği uygulama sürecindeki bir işleyici iş parçacığına gönderme
Eşzamansız API'ler geçersiz bağımsız değişkenler için geçersiz döndürmeli ve yalnızca hata atmalıdır.
Asenkron API'ler, istenen işlemin tüm sonuçlarını sağlanan geri çağırma işlevine bildirmelidir. Bu sayede geliştirici, başarı ve hata işleme için tek bir kod yolu uygulayabilir.
Asenkron API'ler, bağımsız değişkenleri boş olup olmadığını kontrol edip NullPointerException
değerini döndürebilir veya sağlanan bağımsız değişkenlerin geçerli bir aralık içinde olup olmadığını kontrol edip IllegalArgumentException
değerini döndürebilir. Örneğin, 0
ile 1f
aralığında bir float
kabul eden bir işlev, parametrenin bu aralıkta olup olmadığını kontrol edebilir ve aralık dışındaysa IllegalArgumentException
atabilir. Alternatif olarak, kısa bir String
, yalnızca alfanümerik karakterler gibi geçerli bir biçime uygun olup olmadığı açısından kontrol edilebilir. (Sistem sunucusunun hiçbir zaman uygulama işlemine güvenmemesi gerektiğini unutmayın. Her sistem hizmeti, bu kontrolleri sistem hizmetinde de yapmalıdır.)
Diğer tüm hatalar, sağlanan geri çağırma işlevine bildirilmelidir. Bunlarla sınırlı olmamak üzere aşağıdakiler buna dahildir:
- İstenen işlemde terminal hatası
- İşlemi tamamlamak için gereken yetkilendirme 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ının sonlandırılması veya uzak işlemin kullanılamaması
Eşzamansız API'ler 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 işaret etmelidir:
Arayan tarafından sağlanan geri aramalara yapılan sabit referanslar serbest bırakılmalıdır
Asenkron API'lere sağlanan geri çağırmalar, büyük nesne grafiklerine sabit referanslar içerebilir. Bu geri çağırmaya sabit referans tutan devam eden çalışmalar, söz konusu nesne grafiklerinin çöp toplayıcı tarafından toplanmasını engelleyebilir. İptal sırasında bu geri çağırma referansları serbest bırakılarak bu nesne grafikleri, işin tamamlanmasına izin verildiğinden çok daha erken bir zamanda çöp toplama için uygun hale gelebilir.
Arayan için iş yapan yürütme motoru bu işi durdurabilir
Asenkron API çağrıları tarafından başlatılan işler, güç tüketimi veya diğer sistem kaynakları açısından yüksek maliyet taşıyabilir. Arayan kullanıcıların bu çalışmaya artık ihtiyaç duyulmadığında sinyal göndermesine olanak tanıyan API'ler, daha fazla sistem kaynağı tüketmeden önce bu çalışmanın durdurulmasına olanak tanır.
Önbelleğe alınan veya dondurulmuş uygulamalarla ilgili özel hususlar
Geri çağırmaların bir sistem sürecinden kaynaklandığı ve uygulamalara iletildiği asenkron API'leri tasarlarken aşağıdakileri göz önünde bulundurun:
- İşlemler ve uygulama yaşam döngüsü: Alıcı uygulama işlemi önbelleğe alınmış durumda olabilir.
- Önbelleğe alınan uygulamaları dondurma: Alıcı uygulama işlemi dondurulmuş olabilir.
Bir uygulama işleminin önbelleğe alınmış duruma girmesi, etkinlikler ve hizmetler gibi kullanıcı tarafından görülebilen bileşenleri etkin bir şekilde barındırmadığı anlamına gelir. Uygulama, tekrar kullanıcı tarafından görülebileceği için bellekte tutulur ancak bu süre zarfında çalışmamalıdır. Çoğu durumda, uygulama önbelleğe alınmış duruma girdiğinde uygulama geri çağırmalarının gönderilmesini duraklatmalı ve uygulama önbelleğe alınmış durumdan çıktığında göndermeyi devam ettirmelisiniz. Böylece, önbelleğe alınmış uygulama işlemlerinde çalışma yapılmasını önleyebilirsiniz.
Önbelleğe alınmış bir uygulama da donmuş olabilir. Dondurulan uygulamalara sıfır CPU süresi atanır ve bu uygulamalar hiçbir işlem yapamaz. Bu uygulamanın kayıtlı geri çağırma işlevlerine yapılan tüm çağrılar arabelleğe alınır ve uygulamanın dondurulması kaldırıldığında yayınlanır.
Uygulama geri çağırmalarına yönelik arabelleğe alınan işlemler, uygulamanın dondurulması kaldırılıp işleme alındığında eski olabilir. Tampon sonlu olduğundan taşarsa alıcı uygulamanın kilitlenmesine neden olur. Uygulamaların eski etkinliklerle boğulmasını veya arabelleklerinin taşmasını önlemek için, uygulamaların işlemleri dondurulmuşken uygulama geri çağırmalarını dağıtmayın.
İnceleniyor:
- Uygulamanın işlemi önbelleğe alınırken uygulama geri çağırmalarının gönderilmesini duraklatmayı düşünebilirsiniz.
- Uygulamanın süreci dondurulmuşken uygulama geri çağırmalarının gönderilmesini DURAKLATMANIZ gerekir.
Durum izleme
Uygulamaların ne zaman önbelleğe alınmış duruma girip çıktığını izlemek için:
mActivityManager.addOnUidImportanceListener(
new UidImportanceListener() { ... },
IMPORTANCE_CACHED);
Uygulamaların ne zaman dondurulduğunu veya dondurulmadığını izlemek için:
IBinder binder = <...>;
binder.addFrozenStateChangeCallback(executor, callback);
Uygulama geri çağırmalarının gönderilmesini devam ettirme stratejileri
Uygulama, önbelleğe alınmış veya dondurulmuş duruma girdiğinde uygulama geri çağırmalarının gönderilmesini duraklatsanız bile uygulama ilgili durumdan çıktığında, uygulama geri çağırmalarının kaydını silmesine veya uygulama işleminin sona ermesine kadar uygulamanın kayıtlı geri çağırmalarının gönderilmesini devam ettirmeniz gerekir.
Ö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, dondurulduğunda hedef sürece geri çağırma göndermemesini sağlayan RemoteCallbackList
işlevini 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 dondurulmadığında çağrılır.
Uygulamalar, genellikle geri çağırma işlevini kullanarak aldıkları güncellemeleri en son durumun anlık görüntüsü olarak kaydeder. Uygulamaların kalan pil yüzdesini izleyebilmesi için varsayımsal bir API'yi 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 düşünün. Uygulamanın dondurulması kaldırıldığında uygulamaya yalnızca en son durumu göndermeniz ve diğer eski durum değişikliklerini bırakmanız gerekir. Bu yayınlama işlemi, uygulamanın "yetişmesi" için uygulamanın dondurulması kaldırılır kaldırılmaz gerçekleşmelidir. Bunu aşağıdaki şekilde yapabilirsiniz:
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 gönderilen son değeri izleyebilirsiniz. Böylece, uygulamanın dondurulması kaldırıldıktan sonra aynı değerle ilgili bildirim alması gerekmez.
Durum, daha karmaşık veriler olarak ifade edilebilir. Uygulamaların ağ arayüzleri hakkında bilgilendirilmesi için varsayımsal bir API'yi düşünün:
interface NetworkListener {
void onAvailable(Network network);
void onLost(Network network);
void onChanged(Network network);
}
Bir uygulamanın bildirimlerini duraklatırken, uygulamanın en son gördüğü ağ ve durumları hatırlamanız gerekir. Devam ettikten sonra uygulamayı, kaybedilen eski ağlar, kullanıma sunulan yeni ağlar ve durumu değişen mevcut ağlar hakkında bu sırayla bilgilendirmeniz önerilir.
Geri çağırmalar duraklatıldığında kullanılabilir hale getirilen ve daha sonra kaybedilen ağları uygulamaya bildirmeyin. Uygulamalar, dondurulmuş durumdayken gerçekleşen etkinliklerin tam hesabını almamalı ve API dokümanları, açık yaşam döngüsü durumlarının dışında kesintisiz etkinlik akışları sunma sözü vermemelidir. Bu örnekte, uygulamanın ağ kullanılabilirliğini sürekli olarak izlemesi gerekiyorsa uygulama, önbelleğe alınmasını veya dondurulmasını önleyen bir yaşam döngüsü durumunda kalmalıdır.
Özetlemek gerekirse, duraklatma işleminden sonra ve bildirimleri devam ettirmeden önce gerçekleşen etkinlikleri birleştirip kayıtlı uygulama geri çağırma işlevlerine en son durumu kısaca iletmeniz gerekir.
Geliştirici belgeleri ile ilgili dikkat edilmesi gereken noktalar
Asynkron etkinliklerin yayını, gönderenin önceki bölümde gösterildiği gibi yayını bir süreliğine duraklatması veya alıcı uygulamanın etkinliği zamanında işlemek için yeterli cihaz kaynağı almaması nedeniyle gecikebilir.
Geliştiricilerin, uygulamalarının bir etkinlikle ilgili bilgilendirildiği zaman ile etkinliğin gerçekleştiği zaman arasındaki süreyle ilgili varsayımlarda bulunmalarını engelleyin.
API'lerin askıya alınmasıyla ilgili geliştirici beklentileri
Kotlin'in yapılandırılmış eşzamanlılığına aşina geliştiriciler, askıya alan API'lerden aşağıdaki davranışları bekler:
Duraklatma işlevleri, döndürmeden veya atmadan önce ilişkili tüm işleri tamamlamalıdır.
Engellemeyen işlemlerin sonuçları normal işlev dönüş değerleri olarak döndürülür ve hatalar istisnalar atılarak bildirilir. (Bu genellikle geri çağırma parametrelerinin gerekmediği anlamına gelir.)
Askıya alma işlevleri yalnızca yerinde geri çağırma parametrelerini çağırmalıdır
Askıya alma işlevleri, her zaman döndürülmeden önce ilişkili tüm çalışmaları tamamlamalıdır. Bu nedenle, sağlanan 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 bu işlevlere referans vermemelidir.
Aksi belirtilmediği sürece, geri çağırma parametrelerini kabul eden askıya alma işlevleri bağlamı korumalıdır.
Bir işlevin askıya alma işlevinde çağrılması, işlevin CoroutineContext
içinde çalışmasına neden olur. Askıya alma işlevleri, döndürmeden veya atmadan önce ilişkili tüm çalışmaları tamamlamalı ve yalnızca geri çağırma parametrelerini yerinde çağırmalıdır. Bu nedenle, varsayılan olarak bu tür geri çağırmaların, ilişkili dağıtıcıyı kullanarak CoroutineContext
çağıran tarafta ayrıca çalıştırılması beklenir. API'nin amacı, CoroutineContext
çağrısının dışında bir geri çağırma çalıştırmaksa bu davranış açıkça belirtilmelidir.
Askıya alma işlevleri, kotlinx.coroutines iş iptallerini desteklemelidir
Sunulan tüm askıya alma işlevleri, kotlinx.coroutines
tarafından tanımlandığı şekilde iş iptaliyle birlikte çalışmalıdır. Devam eden bir işlemin çağrı işiyle ilgili iş iptal edilirse işlev, arayanın en kısa sürede temizleme yapabileceği ve devam edebileceği şekilde bir CancellationException
ile en kısa sürede 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 bu iptal davranışını varsayılan olarak desteklemediği için doğrudan suspendCoroutine
kullanmamalıdır.
Arka planda (ana veya kullanıcı arayüzü iş parçacığı olmayan) engelleme işlemi gerçekleştiren askıya alma işlevleri, kullanılan dağıtıcıyı yapılandırmanın bir yolunu sağlamalıdır
Engelleme işlevinin, iş parçacıklarını değiştirmek için tamamen askıya alınması önerilmez.
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 kurucu, sınıfın yöntemleri için arka planda çalışma yapmak üzere kullanılan bir CoroutineContext
öğesini kabul edebilir.
İsteğe bağlı bir CoroutineContext
veya Dispatcher
parametresini yalnızca engelleme işlemini gerçekleştirmek için bu dağıtıcıya geçmek üzere kabul edecek olan askıya alma işlevleri, bunun yerine temel engelleme işlevini göstermeli ve işleri seçilen bir dağıtıcıya yönlendirmek için çağıran geliştiricilerin kendi withContext çağrılarını kullanmalarını önermelidir.
İş parçacığı başlatan sınıflar
İş parçacıklarını başlatan sınıflarda, bu başlatma işlemlerini gerçekleştirmek için bir CoroutineScope
olmalıdır. 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ı ima eder.
Başka bir kapsamda eşzamanlı görevler 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 bir suspend fun
göstermek, MyClass
'ın CoroutineScope
yönetmesi gerekmediğinden, suspend fun
'yi çağıranın işlemi kendi bağlamında çağırmasına olanak tanır. İsteklerin işlenmesi seri hale getirilir ve durum genellikle ek senkronizasyon gerektiren sınıf özellikleri yerine handleRequests
yerel değişkenleri olarak mevcut olabilir.
İş parçacıklarını yöneten sınıflar kapatma ve iptal yöntemlerini göstermelidir
Uygulama ayrıntıları olarak iş parçacıklarını başlatan sınıflar, devam eden eşzamanlı görevlerin ana kapsama sızmaması için bu görevlerin temiz bir şekilde kapatılmasını sağlayan bir yöntem sunmalıdır. Bu işlem genellikle, sağlanan bir CoroutineContext
için alt Job
oluşturma şeklinde gerçekleşir:
private val myJob = Job(parent = `CoroutineContext`[Job])
private val myScope = CoroutineScope(`CoroutineContext` + myJob)
fun cancel() {
myJob.cancel()
}
Kullanıcı kodunun, nesne tarafından gerçekleştirilen eşzamanlı çalışmaların tamamlanmasını beklemesine olanak tanımak için bir join()
yöntemi de sağlanabilir.
(Bu, bir işlemi iptal ederek yapılan temizleme çalışmalarını içerebilir.)
suspend fun join() {
myJob.join()
}
Terminal işleminin adlandırılması
Bir nesnenin sahip olduğu ve hâlâ devam eden eşzamanlı görevleri düzgün bir şekilde kapatan yöntemler için kullanılan ad, kapatmanın nasıl gerçekleştiğiyle ilgili davranış sözleşmesini yansıtmalıdır:
Devam eden işlemler tamamlanabilir ancak close()
çağrısı döndükten sonra yeni işlem başlatılamayabilir. Bu durumda close()
işlevini kullanın.
Devam eden işlemler tamamlanmadan önce iptal edilebileceğinde cancel()
simgesini kullanın.
cancel()
çağrısı döndükten sonra yeni işlem başlatılamaz.
Sınıf kurucuları CoroutineScope yerine CoroutineContext'i kabul eder
Nesnelerin doğrudan sağlanan bir üst kapsamda başlatılması yasaklandığında, CoroutineScope
'ün yapıcı 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 bir kurucu parametresi olarak iletmek için oluşturulup daha sonra reddedilecek gereksiz ve yanıltıcı bir sarmalayıcı haline gelir:
// 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`
gözetleyicisi olmalıdır. Bir Empty`CoroutineContext`
değeri, arayandan geldiğinde varsayılan değeri kabul etmekle aynı şekilde ele alındığından bu, 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)
// ...
}