Kotlin eş yordamları, ağ çağrıları veya disk işlemleri gibi uzun süreli görevleri yönetirken uygulamanızın duyarlı olmasını sağlayan net ve basitleştirilmiş eşzamansız kod yazmanıza olanak tanır.
Bu konuda, Android'deki eş yordamlar ayrıntılı bir şekilde açıklanmaktadır. Eş yordamlar hakkında bilginiz yoksa bu konuyu okumadan önce Android'deki Kotlin eş yordamlarını okuduğunuzdan emin olun.
Uzun süreli görevleri yönetin
İşbirlikleri, uzun süreli görevleri yerine getirmek için iki işlem ekleyerek normal işlevler üzerine kuruludur. invoke
(veya call
) ve return
'ye ek olarak eş yordalar suspend
ve resume
ekler:
suspend
, mevcut eş yordasının yürütülmesini duraklatarak tüm yerel değişkenleri kaydeder.resume
, askıya alınmış bir eş yordamı askıya alındığı yerden yürütülmeye devam ediyor.
suspend
işlevlerini yalnızca diğer suspend
işlevlerinden veya yeni bir eş yordam başlatmak için launch
gibi bir eş yordam oluşturucu kullanarak çağırabilirsiniz.
Aşağıdaki örnekte, uzun süreli bir görev için basit bir eş yordam uygulaması gösterilmektedir:
suspend fun fetchDocs() { // Dispatchers.Main
val result = get("https://developer.android.com") // Dispatchers.IO for `get`
show(result) // Dispatchers.Main
}
suspend fun get(url: String) = withContext(Dispatchers.IO) { /* ... */ }
Bu örnekte get()
, ana iş parçacığında çalışmaya devam eder ancak ağ isteğini başlatmadan önce eş yordanı askıya alır. Ağ isteği tamamlandığında get
, ana iş parçacığını bildirmek için bir geri çağırma kullanmak yerine askıya alınmış eş yordasını devam ettirir.
Kotlin, yerel değişkenlerle birlikte hangi işlevin çalıştığını yönetmek için bir yığın çerçevesi kullanır. Bir eş yordamı askıya alırken mevcut yığın çerçevesi kopyalanır ve daha sonra kullanılmak üzere kaydedilir. Devam ettirildiğinde, yığın çerçevesi kaydedildiği yerden tekrar kopyalanır ve işlev tekrar çalışmaya başlar. Kod sıradan bir sıralı engelleme isteği gibi görünse de eş yordam, ağ isteğinin ana iş parçacığını engellemekten kaçınmasını sağlar.
Ana güvenlik için eş yordamları kullan
Kotlin eş yordamları, eş yordam yürütmesi için hangi iş parçacıklarının kullanıldığını belirlemek amacıyla dispatcher'ları kullanır. Kodu ana iş parçacığının dışında çalıştırmak için Kotlin eş yordamlarına Varsayılan veya IO görev dağıtıcıda çalışma yapmasını bildirebilirsiniz. Kotlin'de tüm eş yordamlar, ana iş parçacığında çalışırken bile bir görev dağıtıcıda çalışmalıdır. İşbirlikleri kendilerini askıya alabilir ve bunları devam ettirmekten görev dağıtıcı sorumlu olur.
Kotlin'in nerede çalışması gerektiğini belirlemek için Kotlin, kullanabileceğiniz üç görev dağıtıcı sağlar:
- Dispatchers.Main - Ana Android iş parçacığında bir eş yordam çalıştırmak için bu görev dağıtıcıyı kullanın. Bu, yalnızca kullanıcı arayüzüyle etkileşim kurmak ve hızlı çalışma gerçekleştirmek için kullanılmalıdır. Örnekler arasında
suspend
işlevlerini çağırma, Android kullanıcı arayüzü çerçeve işlemlerini çalıştırma veLiveData
nesnelerini güncelleme yer alır. - Dispatchers.IO - Bu görev dağıtıcı, ana iş parçacığının dışında disk veya ağ G/Ç'si gerçekleştirmek için optimize edilmiştir. Örnekler arasında Oda bileşenini kullanma, dosyalardan okuma veya dosyalara yazma ve tüm ağ işlemlerini çalıştırma yer alır.
- Dispatchers.Default - Bu görev dağıtıcı, ana iş parçacığının dışında CPU yoğun çalışmalar gerçekleştirmek üzere optimize edilmiştir. Örnek kullanım alanları arasında bir listeyi sıralama ve JSON'u ayrıştırma yer alır.
Önceki örnekten devam edersek, get
işlevini yeniden tanımlamak için görev dağıtıcıları kullanabilirsiniz. IO iş parçacığı havuzunda çalışan bir blok oluşturmak için get
gövdesinde withContext(Dispatchers.IO)
çağrısı yapın. Bu blokun içine yerleştirdiğiniz tüm kodlar her zaman IO
görev dağıtıcısı aracılığıyla yürütülür. withContext
işlevinin kendisi bir askıya alma işlevi olduğundan get
işlevi de bir askıya alma işlevidir.
suspend fun fetchDocs() { // Dispatchers.Main
val result = get("developer.android.com") // Dispatchers.Main
show(result) // Dispatchers.Main
}
suspend fun get(url: String) = // Dispatchers.Main
withContext(Dispatchers.IO) { // Dispatchers.IO (main-safety block)
/* perform network IO here */ // Dispatchers.IO (main-safety block)
} // Dispatchers.Main
}
Eş yordamlar sayesinde iş parçacıklarını ayrıntılı bir denetimle dağıtabilirsiniz. withContext()
, geri çağırma olmaksızın herhangi bir kod satırının ileti dizisi havuzunu kontrol etmenize olanak sağladığından, bu kodu veritabanından okuma veya ağ isteği gerçekleştirme gibi çok küçük işlevlere uygulayabilirsiniz. Her işlevin main güvenli olduğundan emin olmak için withContext()
kullanmanız iyi bir uygulamadır. Diğer bir deyişle, işlevi ana iş parçacığından çağırabilirsiniz. Bu şekilde, çağrıyı yapan kişinin işlevi yürütmek için hangi iş parçacığının kullanılması gerektiğini asla düşünmesine gerek kalmaz.
Önceki örnekte fetchDocs()
, ana iş parçacığında yürütülür. Ancak arka planda ağ isteği gerçekleştiren get
işlemini güvenli bir şekilde çağırabilir.
Eş yordamlar suspend
ve resume
özelliklerini desteklediğinden ana iş parçacığındaki eş yordam, withContext
bloğu biter bitmez get
sonucuyla devam ettirilir.
withContext() performansı
withContext()
, geri çağırmaya dayalı eşdeğer bir uygulamaya kıyasla fazladan ek yük sağlamaz. Ayrıca bazı durumlarda withContext()
çağrılarını, geri çağırmaya dayalı eşdeğer bir uygulamanın ötesinde optimize etmek mümkündür. Örneğin, bir işlev bir ağa on çağrı yapıyorsa Kotlin'e, dış bir withContext()
kullanarak iş parçacıklarını yalnızca bir kez değiştirmesini söyleyebilirsiniz. Ardından, ağ kitaplığı withContext()
hizmetini birden çok kez kullansa bile aynı dağıtıcıda kalır ve iş parçacıkları arasında geçiş yapmaktan kaçınır. Buna ek olarak Kotlin, mümkün olduğunda iş parçacığı geçişlerini önlemek için Dispatchers.Default
ile Dispatchers.IO
arasında geçişi optimize eder.
Eş yordam başlat
Eş yordamları şu iki yöntemden biriyle başlatabilirsiniz:
launch
yeni bir eş yordam başlatır ve sonucu arayana döndürmez. "Ateş ve unut" olarak kabul edilen tüm çalışmalarlaunch
kullanılarak başlatılabilir.async
yeni bir eş yordam başlatır veawait
adlı askıya alma işleviyle sonuç döndürmenize olanak tanır.
Normal bir işlev await
çağıramaz. Bu nedenle, genellikle normal işlevden yeni bir eş yordamı launch
yapmanız gerekir. async
öğesini yalnızca başka bir eş yordamın içindeyken veya bir askıya alma işlevinin içindeyken ve paralel ayrıştırma gerçekleştirirken kullanın.
Paralel ayrıştırma
Bir suspend
işlevi içinde başlatılan tüm eş yordamlar, bu işlev geri döndüğünde durdurulmalıdır. Bu nedenle, geri dönmeden önce bu eş yordamların bittiğini garanti etmeniz gerekir. Kotlin'de yapılandırılmış eşzamanlılık ile bir veya daha fazla eş yordam başlatan bir coroutineScope
tanımlayabilirsiniz. Daha sonra, await()
(tek bir eş yordam için) veya awaitAll()
(birden çok eş yordam için) kullanarak bu eş yordamlarının işlevden dönmeden önce bittiğini garanti edebilirsiniz.
Örnek olarak, iki belgeyi eşzamansız olarak getiren bir coroutineScope
tanımlayalım. Ertelenen her referansta await()
yöntemini çağırarak her iki async
işleminin de değer döndürmeden önce tamamlanmasını garanti ederiz:
suspend fun fetchTwoDocs() =
coroutineScope {
val deferredOne = async { fetchDoc(1) }
val deferredTwo = async { fetchDoc(2) }
deferredOne.await()
deferredTwo.await()
}
awaitAll()
öğesini koleksiyonlarda da aşağıdaki örnekte gösterildiği gibi kullanabilirsiniz:
suspend fun fetchTwoDocs() = // called on any Dispatcher (any thread, possibly Main)
coroutineScope {
val deferreds = listOf( // fetch two docs at the same time
async { fetchDoc(1) }, // async returns a result for the first doc
async { fetchDoc(2) } // async returns a result for the second doc
)
deferreds.awaitAll() // use awaitAll to wait for both network requests
}
fetchTwoDocs()
, async
ile yeni eş yordamları başlatsa bile işlev, geri dönmeden önce başlatılan eş yordalarının bitmesini beklemek için awaitAll()
kullanır. Bununla birlikte, awaitAll()
adını vermemiş olsak bile coroutineScope
oluşturucunun, fetchTwoDocs
adlı eş yordamı tüm yeni eş postalar tamamlanana kadar devam ettirmediğini unutmayın.
Buna ek olarak coroutineScope
, eş yordamların oluşturduğu istisnaları yakalar ve bunları tekrar arayana yönlendirir.
Paralel ayrıştırma hakkında daha fazla bilgi için Askıya alma işlevleri oluşturma bölümüne bakın.
Eş yordam kavramları
Ortak Kapsam
CoroutineScope
, launch
veya async
kullanarak oluşturduğu tüm eş yordamları takip eder. Devam eden işler (ör. devam eden ortak çalışmalar), dilediğiniz zaman scope.cancel()
çağrısı yapılarak iptal edilebilir. Android'de bazı KTX kitaplıkları, belirli yaşam döngüsü sınıfları için kendi CoroutineScope
sağlar. Örneğin, ViewModel
öğesinde viewModelScope
, Lifecycle
öğesinde lifecycleScope
var.
Ancak, bir görev dağıtıcının aksine, CoroutineScope
eş yordamları çalıştırmaz.
viewModelScope
, Koritinler ile Android'de arka plan ileti dizisi oluşturma bölümündeki örneklerde de kullanılmıştır.
Ancak uygulamanızın belirli bir katmanındaki eş yordamların yaşam döngüsünü kontrol etmek için kendi CoroutineScope
kodunuzu oluşturmanız gerekirse aşağıdaki gibi oluşturabilirsiniz:
class ExampleClass {
// Job and Dispatcher are combined into a CoroutineContext which
// will be discussed shortly
val scope = CoroutineScope(Job() + Dispatchers.Main)
fun exampleMethod() {
// Starts a new coroutine within the scope
scope.launch {
// New coroutine that can call suspend functions
fetchDocs()
}
}
fun cleanUp() {
// Cancel the scope to cancel ongoing coroutines work
scope.cancel()
}
}
İptal edilen bir kapsam daha fazla eş yordam oluşturamaz. Bu nedenle, scope.cancel()
yöntemini yalnızca yaşam döngüsünü kontrol eden sınıf kaldırılırken çağırmanız gerekir. viewModelScope
kullanıldığında ViewModel
sınıfı, ViewModel'in onCleared()
yönteminde kapsamı sizin için otomatik olarak iptal eder.
İş
Job
, eş yordamı gösteren bir herkese açık kullanıcı adıdır. launch
veya async
ile oluşturduğunuz her eş yordam, eş yordamı benzersiz şekilde tanımlayan ve yaşam döngüsünü yöneten bir Job
örneği döndürür. Aşağıdaki örnekte gösterildiği gibi, yaşam döngüsünü daha ayrıntılı bir şekilde yönetmek için Job
öğesini CoroutineScope
öğesine de iletebilirsiniz:
class ExampleClass {
...
fun exampleMethod() {
// Handle to the coroutine, you can control its lifecycle
val job = scope.launch {
// New coroutine
}
if (...) {
// Cancel the coroutine started above, this doesn't affect the scope
// this coroutine was launched in
job.cancel()
}
}
}
Ortak Bağlam
CoroutineContext
, aşağıdaki öğe grubunu kullanarak bir eş yordasının davranışını tanımlar:
Job
: Eş yordamın yaşam döngüsünü kontrol eder.CoroutineDispatcher
: Göndermeler ilgili iş parçacığına çalışır.CoroutineName
: Hata ayıklama için yararlı olan eş yordamın adı.CoroutineExceptionHandler
: Yakalanmamış istisnaları işler.
Bir kapsam dahilinde oluşturulan yeni eş yordamlar için yeni eş yordasına yeni bir Job
örneği atanır ve diğer CoroutineContext
öğeleri de kapsayıcı kapsamdan devralınır. launch
veya async
işlevine yeni bir CoroutineContext
geçirerek devralınan öğeleri geçersiz kılabilirsiniz. Yeni Job
örneği her zaman yeni bir eş kritere atanır. Bu nedenle, Job
öğesinin launch
veya async
öğesine iletilmesinin herhangi bir etkisi olmadığını unutmayın.
class ExampleClass {
val scope = CoroutineScope(Job() + Dispatchers.Main)
fun exampleMethod() {
// Starts a new coroutine on Dispatchers.Main as it's the scope's default
val job1 = scope.launch {
// New coroutine with CoroutineName = "coroutine" (default)
}
// Starts a new coroutine on Dispatchers.Default
val job2 = scope.launch(Dispatchers.Default + CoroutineName("BackgroundCoroutine")) {
// New coroutine with CoroutineName = "BackgroundCoroutine" (overridden)
}
}
}
Ek eş yordam kaynakları
Daha fazla eş yordam kaynağı için aşağıdaki bağlantılara bakın: