Kotlin Coroutines Temelleri – Basit Android Uygulaması Demosu

Bu basit Android uygulamasını, Kotlin eşyordamlarının temel kullanımlarını anlamama yardımcı olması için oluşturdum. Uygulama, eşyordam işlerinin nasıl oluşturulacağını ve bunların eşzamanlı olarak nasıl çalıştırılacağını gösterir. Muhtemelen kullanım durumlarının %100’ünü, belki de en az %90’ını kapsamayacaktır?
Uygulama ayrıca basit kullanır MVVM mimarisi (olmadan Model
tam olarak).
Uygulamaya Genel Bakış
Bu uygulamada 3 düğme ve 2 ekran metni (sol ve sağ) kullanıcı arayüzü vardır.
- Başlatmak ve zaman uyumsuz düğmeler her ikisini de günceller sol metin ve doğru metin kullanıcı arayüzü aynı anda
- sol metin ve doğru metin başlatıldı -1 geçersiz değer olarak
- Ne zaman Başlatmak veya zaman uyumsuz butonuna tıklanır, güncellemek için eşyordamlar oluşturulur sol metin kullanıcı arayüzü 0 → 9 ve doğru metin kullanıcı arayüzü 10 → 19
- Eğer İptal butonuna tıklandığında, her iki metnin de güncellenmesi durdurulur ve değer olarak ayarlanır. -1.
- Eğer hayırsa İptal butonuna tıklanır, her iki metin de son değere kadar güncellenmeye devam eder. 9 ve 19
Eşyordamlar oluşturmanın 2 yolu vardır:
CoroutineScope.launch
CoroutineScope.async
CoroutineScope.launch
Bir eşyordam oluşturmak için, CoroutineScope
ilk. İçinde ViewModel
, CorotineScope
zaten oluşturuldu (yani viewModelScope
). Bu nedenle, kendiniz oluşturmak yerine kullanmanız şiddetle tavsiye edilir. Bir fayda, tüm CoroutineScope
çocuklar ne zaman otomatik olarak iptal edilecek ViewModel
yok edildi.
currentJob = viewModelScope.launch {
val job1 = launch {
...
}
val job2 = launch {
...
}
job1.join()
job2.join()
...
}
CoroutineScpoe.launch
engellemeyen bir işlevdir ve geri döner Job
hemen. Eşzamanlılık elde etmek için arayabiliriz CoroutineScpoe.launch
aynı eşyordam kapsamında birden çok kez. job1
güncellemekten sorumludur. sol metin kullanıcı arayüzü ve job2
güncellemekten sorumludur. doğru metin kullanıcı arayüzü
İşin tamamlanmasını beklemek istiyorsanız, aramanız gerekir. Job.join()
askıya alma işlevi. Bu, bir sonraki satıra geçmeden önce işin tamamlanmasını bekleyecektir.
CoroutineScope.async
Döndürülen değeri beklemek istediğimiz eşyordamı oluşturmak için kullanıyoruz CoroutineScope.async
.
Benzer CoroutineScpoe.launch
, CoroutineScope.async
engellemeyen bir fonksiyondur. geri dönmek yerine Job
geri döner Deferred<T>
. içindeki son satır async block
dönüş türüdür T
. Örneğin, getData()
İadeler Int
Böylece T
dır-dir Int
tip.
viewModelScope.launch {
val deferred = async {
...
getData()
}
data.value = deferred.await()
...
}
Kullanmak yerine Job.join()
sen ara Deferred<T>.awailt()
için beklemek CoroutineScope.async
bitirmek ve ayrıca değeri döndürmek için getData()
.
CoroutineScope.withContext()
Varsayılan olarak, eşyordamlar ana/UI iş parçacığında çalıştırılır. Ana/UI iş parçacığını engellememesi için uzun süren görevleri farklı iş parçacığına taşımalısınız.
Farklı bir konuya geçmek için şunları belirtirsiniz: CoroutineDispatcher
. İşte ortak önceden tanımlanmış CoroutineDispatcher
kullanabileceğimiz:
Dispatchers.Main
– ana/UI iş parçacığıDispatchers.Default
– CPU işlem dizisiDispatchers.IO
– IO veya ağ işlemi iş parçacığı
Kendi konunuzu kullanmak için yeni bir konu/yeni konu oluşturabilirsiniz. CoroutineDispatcher
kullanarak newSingleThreadContext("MyOwnThread")
. Çoğu zaman önceden tanımlanmış CoroutineDispatcher
yeterlidir.
ile bir eşyordam oluştururken launch
veya async
belirtebilirsiniz CoroutineDispatcher
.
viewModelScope.launch {
launch(Dispatchers.Default) {
loadData()
}
async(Dispatchers.Default) {
loadData()
}
}
Ancak, kullanmak daha iyi bir çözümdür. CoroutineScope.withContext()
belirtmek yerine askıya alma işlevinde CoroutineDispatcher
koroutin oluşturma sırasında. Bu, askıya alma işlevinin ana/UI iş parçacığından çağrılmasını güvenli hale getirdiği için önerilir.
private suspend fun loadData() {
withContext(Dispatchers.Default) {
...
}
}
lütfen aklınızda bulundurun
CoroutineScope.withContext()
yeni bir eşyordam oluşturmaz. Eşyordamları farklı bir iş parçacığına taşır.
Job.cancelAndJoin()
Bir eşyordam işini iptal etmek için Job.cancel()
ve Job.join()
. Çoğu zaman, sadece arayabilirsin Job.cancelAndJoin()
. Lütfen bunu not al Job.cancelAndJoin()
bir askıya alma işlevidir. Bu yüzden onu eşyordamın içinde aramanız gerekiyor.
fun onCancelButtonClick() {
if (currentJob == null) return
viewModelScope.launch() {
currentJob!!.cancelAndJoin()
}
}
currentJob
daha önce oluşturulmuş mevcut bir eşyordam işidir.
kotlinx.coroutines.yield()
Dikkat edilmesi gereken önemli bir nokta eşyordam iptali işbirlikçidir. Bir eşyordam işbirlikçi olmayan iptal ise, onu iptal etmemizin hiçbir yolu yoktur. Eşyordam tamamlanıncaya kadar çalışmaya devam edecek, ancak Job.cancel()
arandı.
Bir eşyordam iptali kooperatifi yapmak için şunları kullanabilirsiniz:
CoroutineScope.isActive
kotlinx.coroutines.yield()
CoroutineScope.isActive
gereklidir CoroutineScope
çağrılacak nesnedir ve eşyordamdan çıkmak için mantık eklemeniz gerekir, bu nedenle daha az esnektir. Dan beri yield()
herhangi bir askıya alma işlevinde çağrılabilir, ben şahsen kullanmayı tercih ederim.
lütfen not edin
kotlinx.coroutines.delay()
ayrıca eşyordam iptalini kooperatif yapın.
Örneğin, aşağıdaki gibi uzun süredir devam eden bir göreviniz varsa, eşyordam hiçbir görevi yerine getirmeyecektir. Job.cancel()
rica etmek.
private suspend fun simulateLongRunningTask() {
repeat(1_000_000) {
Thread.sleep(100)
}
}
kabul etmesini sağlamak için Job.cancel()
istek, sadece eklemeniz gerekir yield()
.
private suspend fun simulateLongRunningTask() {
repeat(1_000_000) {
Thread.sleep(100)
yield()
}
}
kotlinx.coroutines.JobCancellationException
Bir eşyordam iptali kabul edildiğinde, kotlinx.coroutines.JobCancellationException
istisna atılacaktır. İstisnayı yakalayabilir ve biraz temizlik yapabilirsiniz.
currentJob = viewModelScope.launch {
try {
val job1 = launch {
...
}
val job2 = launch {
...
}
job1.join()
job2.join()
} catch (e: Exception) {
currentJob = null
}
}
kotlinx.coroutines.coroutineContext
Eşyordamı ayıklamak için, günlüğe kaydetme en kolay yoldur. kotlinx.coroutines.coroutineContext
oturum açmak için çok kullanışlıdır. Eşyordam ve iş parçacığı bilgilerini sağlar.
Lütfen bunun yalnızca askıya alma işlevinden çağrılabilen bir askıya alma özelliği olduğunu unutmayın.
Nın bir örneği Utils.log()
sarmak için yardımcı programı askıya alma işlevi .Log.d()
:
object Utils {
suspend fun log(tag: String, msg: String) {
Log.d(tag, "$coroutineContext: $msg")
}
}
Utils.log("ViewModel", "======= Created launch coroutine - onButtonClick() =======")
Logcat çıktısı örneği:
D/ViewModel: [StandaloneCoroutine{Active}@5261b32, Dispatchers.Main.immediate]: ======= Created launch coroutine - onButtonClick() =======
Bazı düşünceler
Şimdiye kadar, tüm benim Kişisel projeler yukarıdaki tüm eşyordam kullanım durumlarını kullanmayın. sadece kullanırım CoroutineScope.launch
ve CoroutineScope.withContext()
bu da istediğimi elde etmem için yeterli. Bir eşyordamı iptal etmem bile gerekmiyor, ancak istersem yapabilirim ve uygulamalar hala mükemmel çalışıyor.
[Update: April 13, 2022]: kullandım joinAll()
kodu sırayla çalıştırmak yerine birkaç ağ aramasını paralel hale getirin. Aşağıdaki örnek:
private suspend fun fetchArticlesFeed() : List<ArticleFeed> = coroutineScope {
val results = mutableListOf<ArticleFeed>()
val jobs = mutableListOf<Job>()
for(url in urls) {
val job = launch {
val xmlString = webService.getXMlString(url)
val articleFeeds = FeedParser().parse(xmlString)
results.addAll(articleFeeds)
}
jobs.add(job)
}
jobs.joinAll()
return@coroutineScope results
}
Kaynak kodu
GitHub Deposu: Demo_CoroutinesTemel Bilgiler