Kotlin Coroutines

Firuze Gümüş
3 min readJun 26, 2020

Simplify threading with Kotlin Coroutines!

Programlama dillerinin başlangıcından itibaren var olan ancak Kotlin diline 1.3 versiyonuyla eklenen “Coroutines” asenkron işlemleri basit bir şekilde gerçekleştirmek için kullanılır.

Birkaç threadi birbirleriyle etkileşimli olarak çalıştırmanın yönetilmesi ne kadar karmaşık bir işlem olduğunu, memory hatalarına nasıl davetiye çıkardığını deneyimlediyseniz siz de coroutines yapısını benim gibi çok seveceksiniz.

Kotlin Coroutines iki temel soruna çözüm olarak ortaya çıkmıştır.

  1. Uzun süren ve main threadi bloklayan işlemler için kısa ve etkili bir çözümdür. Bir milyon coroutine scopunu aynı anda launch edip her birine birtakım işlemler yaptırabilirsiniz. Emin olun Out Of Memory hatası almayacaksınız.
  2. İyi kurgulanmış suspend fonksiyonları main threadden çağırmak güvenilirdir. (Main-safety) Android’de uygulama geliştirirken UI işlemleri için Main threadi kullanmak durumunda kalıyoruz. Bu nedenle coroutine Android geliştirme için biçilmiş kaftan diyebiliriz.

Bir milyon kez çalıştırmak konusunda gayet ciddiyim. Aşağıdaki kod blogunda olduğu gibi Main threadi blocklayan bir coroutine scope içerisinde bile 1.000.000 kez launch işlemi yaptırıp ekrana mesaj yazdırmayı deneyebilirsiniz. Ekrana yazdırması sadece birkaç saniye alıyor. Aynı işlemi threadle yapmayı denemenizi tavsiye etmem. Uygulamanızın crash olması muhtemel. Performans anlamında ne kadar elverişli bir yapıyla karşı karşıya olduğunuzu bir düşünün.

fun main()
{
runBlocking{
repeat(1_000_000)
{
launch {
println("Hello Coroutine! So glad to know you!:)")
}
}
}
}

Coroutines’te regular fonksiyonlarda olmayan iki method vardır:

  • suspend — Mevcut coroutine’in çalışmasını pause duruma alır. Mevcuttaki durumu locale kaydeder.
  • resume —Pause durumda olan coroutine’i kaldığı yerden devam ettirir.

Burda karıştırılan bir konu varsa şudur ki : Suspendin anlamı background değil, coroutines main threadde çalışır. Ancak main threadde çalışmasına rağmen network istekleri, JSON parse , databaseden birşeyler okuma ya da yazma gibi uzun süren maliyetli işlemleri yaparken main threadi bloke etmez.

Coroutineleri kullanırken sağlamamız gereken üç durum var :

  1. İhtiyacımızın kalmadığı görevleri iptal etmek gerekir.
  2. Coroutineler çalışırken takibinin ilgili scope tarafından yapılması gerekir.
  3. Bir coroutine fail olduğunda hata bildirimi yapılması gerekir.

Her coroutineScope kendi içinde üretilen coroutine’leri takip etmeye yarar.

Bir coroutinescope başlatmanın iki yolu var :

  1. launch : “Fire and Forget” diye tabir ettiğimiz gibi launch keywordu yeni bir coroutine başlatacak ve sonucunda herhangi birşey döndürmeyecek.
  2. async : Yeni bir coroutine başlatır ve suspend fonksiyon olan await ile birlikte bir sonuç döndürür. Await bize defered bir değer döndürür. Coroutine tarafından bize dönecek bu değere ihtiyacımız varsa await kullanımını tercih ederiz. Dolayısıyla bu işleyişi bloke eden ve dönüş değerine ulaşıncaya dek bizi bekleten bir çağrıdır. Bazen uygulamada bir değere ulaşmadan bir sonraki stepe geçmememiz gerekebilir. Bu gibi durumlar için tasarlanmıştır.

Bir coroutineScope içerisinde launch dediğimiz zaman scope içerisinde yeni bir coroutine oluştururuz. Her scope kendi içerisinde başlatılan tüm coroutine’leri takip edip iptal edebilir.

scope.launch {
// launch : Scope içerisinde yeni bir coroutine oluşturur.
getData() // suspend fonksiyondur.
}

Peki fazla uzatmadan coroutines nerede nasıl çağrılmalı?

Best practice View Model içerisinde coroutine çağrısı yapmaktır. Coroutinescope.launch aslında bize bir Job döndürür ve bu jobın işleri bittiği zaman cancel edilmesi işlemi bizim kontrolümüzde olmalıdır. Dolayısıyla ViewModel onCleared olduğunda jobında cancel edilmesi gerekir.

var job : Job? = null
fun
getAllPhotoItems() {
job = CoroutineScope(Dispatchers.IO).launch {
val
photoItems = repository.getAllPhotoItems()
withContext(Dispatchers.Main) {
//photoItemsLiveData.postValue(photoItems)
photoItemsLiveData.value = photoItems
}
}
}

override fun onCleared() {
super.onCleared()
job?.cancel()
}

Jobın yönetimiyle de uğraşmak istemiyorum diyorsanız viewModelScope imdadımıza koşuyor. ViewModel içerisinde CoroutineScope’dan extend olan viewModelScope adı altında çok seveceğimiz bir coroutine tanımlı olarak gelmektedir. ViewModel onCleared olduğunda kendisiyle başlatılan coroutinesScope’daki tüm coroutineleri otomatik olarak iptal eder. Böylece takibini yapamadığımız yığınla metod birikerek memory leake neden olmaz.

Coroutine’i spesifik bir threadde çalıştırmak istiyorsak Dispatcher tanımlaması yapıyoruz. Hiçbir dispatcher belirtmediğimiz takdirde Main threadde çalışacaktır. Yourucu database ya da network işlemlerinde IO threadini kullanmamız gerekiyor. Repositoryden dönen sonucu alıp livedataya setlemek gibi main threade paslamam gereken işlemleri yapmak için ise withContext(Dispatchers.Main) blogunu kullanıyorum.

class PhotoViewModel(application: Application) : BaseViewModel(application) {var photoItemsLiveData = MutableLiveData<List<Photo>>()   
var repository = DBRepository(application)

fun getAllPhotoItems() {
viewModelScope.launch(Dispatchers.IO)
{
val
photoItems = repository.getAllPhotoItems()
withContext(Dispatchers.Main) {
photoItemsLiveData
.value = photoItems
}
}
}
}

Room metodlarını regular suspend metodu olarak tanımlıyoruz.

Repository :

suspend fun getAllPhotoItems() : List<Photo>
{
return editorDao.getAllPhotoItems()
}

EditorDao (Data Access Object) interface :

@Query("SELECT * FROM photo")
suspend fun getAllPhotoItems() : List<Photo>

Basitçe MVVM tasarım deseninde Coroutines kullanımından bahsetmeye çalıştım. Kullanmanızı şiddetle tavsiye ediyorum :)

Bir sonraki yazıda görüşmek üzere

Kodla kalın!

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

Firuze Gümüş
Firuze Gümüş

No responses yet

Write a response