Android -Kotlin Coroutines

Recep Yeşilkaya
Arabam Labs
Published in
6 min read1 day ago

Herkese selamlar, bugün Kotlin Coroutines’i temel seviyeden başlayıp ileri seviyelere kadar inceleyeceğiz. Kahveler hazırsa başlayalım☕️

Kotlin Coroutines Nedir?

Kotlin coroutines, asenkron ve paralel programlamayı basit ve okunabilir hale getiren bir kütüphanedir. Coroutines, uzun süren görevleri (örneğin ağ çağrıları veya veri tabanı işlemleri) ana thread’i bloke etmeden gerçekleştirmenize olanak tanır. Peki paralel programlama ve asekron programlama nedir?

Paralel ve Asekron Programlama

Paralel ve asenkron programlama, kullanıcı arayüzünü bloke etmeden arka planda uzun süren işlemleri gerçekleştirmenize olanak tanır, böylece uygulamanızın kullanıcı deneyimini ve performansını önemli ölçüde artırır.

Paralel programlama, bir işlemin alt parçalara bölünerek bu parçaların aynı anda farklı thread’lerde çalıştırılmasıdır. Bu, işlem süresini önemli ölçüde azaltabilir ve yüksek performans gerektiren uygulamalarda büyük avantaj sağlar.

Asenkron programlama, kullanıcı arayüzünü (UI) bloke etmeden arka planda uzun süren işlemleri gerçekleştirir. Bu, uygulamanın kullanıcı etkileşimlerine hızlı yanıt vermesini sağlar ve donma veya yanıt vermeme sorunlarını önler. Kullanıcı deneyiminin dışında Performans ve Verimlilik, Kaynak Yönetimi, Parallelism ve Concurrency konularında bir çok avantaja sahiptir.

Light Thread (Hafif İş Parçacığı), genellikle coroutine’ler ile ifade edilen bir terimdir. Geleneksel iş parçacıklarına (threads) göre çok daha az sistem kaynağı tüketirler ve daha hızlı başlatılırlar. Bu nedenle, daha verimli ve ölçeklenebilir bir asenkron programlama yöntemi sunarlar.

Örneğin, coroutine ile 1 milyon light thread kullanarak paralel bir işlem yapılabilir. Aynı işlemi geleneksel thread’leri kullanarak yaparsanız yanıt almanız pek mümkün değildir.

Kotlin Coroutines vs RxJava

Android uygulama geliştirmede asenkron işlemleri yönetmek için hem Kotlin Coroutines hem de RxJava yaygın olarak kullanılmaktadır. Kotlin Coroutines, sade ve anlaşılır bir yapıya sahip olması nedeniyle yeni başlayanlar ve hızlı geliştirme süreçleri için daha uygun olabilirken, RxJava, geniş özellik seti ve esnek yapısı ile karmaşık ve büyük projelerde tercih edilebilir. Uygulamanızın gereksinimlerine göre doğru seçimi yaparak, performans ve verimlilik açısından en iyi sonucu elde edebilirsiniz.

Coroutine:

  • Daha basit ve okunabilir API
  • Düşük bellek kullanımı ve yüksek performans
  • Kolay öğrenme eğrisi
  • Standart hata yönetimi

RxJava:

  • Fonksiyonel programlama prensiplerine dayalı karmaşık API
  • Yüksek performanslı ancak daha fazla bellek tüketimi
  • Dik öğrenme eğrisi
  • Karmaşık hata yönetimi

Coroutines Kullanarak Asenkron Kod Yazma

suspend fonksiyon, normal bir fonksiyon gibi tanımlanır, ancak suspend anahtar kelimesi ile belirtilir. Bu fonksiyonlar yalnızca başka bir coroutine bloğunda veya suspend fonksiyon içinde çağrılabilir.

suspend Fonksiyonların Arkasındaki Çalışma Mekanizması

Suspend fonksiyonlar, coroutine'lerin duraklatılmasını ve yeniden başlatılmasını sağlayan bir mekanizmaya dayanır. Bu mekanizma sayesinde uzun süren işlemler ana thread'i bloke etmeden gerçekleştirilebilir.

  1. Coroutine Dispatcher: suspend fonksiyonlar belirli bir dispatcher üzerinde çalışır. Dispatchers, coroutine'lerin hangi thread üzerinde çalışacağını belirler.
  2. Continuation: Bir suspend fonksiyon çağrıldığında, fonksiyonun devamı (continuation) oluşturulur. Bu continuation, fonksiyon duraklatıldığında nerede kaldığını hatırlar ve yeniden başlatıldığında kaldığı yerden devam eder.
  3. Suspension Point: delay gibi bir suspend fonksiyon çağrıldığında, coroutine duraklatılır ve suspension point oluşturulur. Bu, coroutine'in duraklatılmasını ve başka bir işleme geçilmesini sağlar.
  4. Resumption: Belirli bir süre veya koşul gerçekleştiğinde, duraklatılmış coroutine yeniden başlatılır ve kaldığı yerden devam eder.

Daha iyi anlamak için bu linkteki videoyu izleyebilirsiniz.

Kotlin Coroutines Temelleri

Coroutine Scope

Coroutine scope, bir veya daha fazla coroutine’in yaşam döngüsünü yönetir. Scope, coroutine’leri başlatmak ve iptal etmek için kullanılır. Bir scope, genellikle belirli bir yaşam döngüsü ile ilişkilendirilir (örneğin, bir Android Activity veya ViewModel).

Scope, coroutine'leri başlatmak için kullanılan launch veya async gibi builder fonksiyonları tarafından kullanılır.

Coroutine Scope: Uygulama Alanları ve Kullanım Örnekleri

1.viewModelScope:viewModelScope, Jetpack ViewModel içinde coroutine'leri başlatmak için kullanılan bir scope'dur. ViewModel'ın yaşam döngüsüne bağlıdır ve ViewModel yok edildiğinde, bu scope içindeki tüm coroutine'ler otomatik olarak iptal edilir.

2. lifecycleScope: lifecycleScope, Android Lifecycle sahip bileşenlerde (Activity, Fragment) coroutine'leri başlatmak için kullanılır. Bu scope, bileşenin yaşam döngüsüne bağlıdır ve bileşen durdurulduğunda coroutine'ler otomatik olarak iptal edilir.

Yaşam Döngüsü Durumlarına Göre Çalışan Coroutine’lerin Kullanımları

repeatOnLifecycle, belirli bir yaşam döngüsü durumu boyunca tekrar eden işler yapmayı kolaylaştırır. Bu, coroutine'lerin güvenli bir şekilde başlatılmasını ve durdurulmasını sağlar, böylece bellek sızıntıları ve gereksiz kaynak kullanımı önlenir.

  • Lifecycle.State.CREATED:Bileşenin oluşturulması sırasında ve kullanıcı ile etkileşime girmeden önce yapılması gereken işlemler için kullanışlıdır. Örneğin, başlangıç verilerini yüklemek veya bileşen hazırlıklarını yapmak için kullanılabilir.Bu blok created durumunda tekrar eder ve destroyedolduğunda durur.
  • Lifecycle.State.STARTED: Bileşenin ekranda görünür hale gelmesiyle başlatılması gereken işlemler için kullanılır. Örneğin, UI güncellemeleri veya görünür bileşenlerle etkileşimler için idealdir. Bu blok started durumunda tekrar eder ve stoppedolduğunda durur.
  • Lifecycle.State.RESUMED: Bileşenin kullanıcı ile tamamen etkileşimde olduğu durumlarda kullanılır. Kullanıcının görsel olarak etkileşimde olduğu ve UI’nın tam olarak görünür olduğu durumlarda yapılması gereken işlemler için kullanılır. Bu blok resumed tekrar eder ve pausedolduğunda durur.

3. GlobalScope:GlobalScope, uygulama yaşam döngüsü boyunca devam eden coroutine'leri başlatmak için kullanılır. Ancak, bu scope dikkatli kullanılmalıdır çünkü coroutine'ler uygulama kapanana kadar çalışır ve bellek sızıntılarına neden olabilir.

4. CoroutineScope:CoroutineScope, belirli bir context ile coroutine'leri başlatmak için kullanılan genel bir scope'dur. CoroutineScope, Job ve Dispatcher gibi context öğeleri ile yapılandırılabilir.

Coroutine Context

CoroutineContext, coroutine'lerin çalıştırıldığı ortamı tanımlar ve coroutine'in davranışını etkileyen çeşitli öğelerden oluşur. Bu öğeler şunları içerir:

  • Job: Coroutine’in yaşam döngüsünü ve iptal edilebilirliğini yönetir.
  • Dispatcher: Coroutine’in hangi iş parçacığında çalışacağını belirler.
  • ExceptionHandler: Coroutine içindeki istisnaları (exceptions) yakalar ve işler.

CoroutineContext arayüzü, bu öğelerin birleşiminden oluşur. En yaygın kullanılan context öğesi, coroutine'in hangi iş parçacığında çalışacağını belirleyen Dispatcher'dır (Dispatchers.IO, Dispatchers.Main, Dispatchers.Default, Dispatchers.Unconfined).

Dispatcher’lar

  • Dispatchers.Main: Coroutine'lerin ana iş parçacığında (UI thread) çalışmasını sağlar. UI güncellemeleri ve kullanıcı etkileşimleri için kullanılır.
  • Dispatchers.IO: Girdi/Çıktı (I/O) işlemleri için optimize edilmiştir. Ağ istekleri, dosya okuma/yazma gibi işlemler için kullanılır.
  • Dispatchers.Default: CPU yoğun işlemler için optimize edilmiştir. Yoğun hesaplamalar ve paralel işlemler için kullanılır.
  • Dispatchers.Unconfined: Coroutine'i başlatıldığı iş parçacığında çalıştırır. İlk askıya almanın ardından, devam eden coroutine, bir sonraki iş parçacığında çalışabilir.

Coroutine Context Özelleştirme

  • Bir coroutine context’i, farklı context öğelerini birleştirerek özelleştirebiliriz Örneğin, bir coroutine’i belirli bir iş parçacığında çalıştırırken aynı zamanda bir Job ile ilişkilendirebiliriz.
  • CoroutineExceptionHandler, coroutine'lerdeki istisnaları (exceptions) yakalamak ve işlemek için kullanılır. Bu, coroutine'lerin hata durumlarında güvenli bir şekilde kapanmasını sağlar.

Bu öğeleri birleştirerek coroutine’lerin çalışma ortamını (context) özelleştirebilir ve belirli ihtiyaçlara göre optimize edebilirsiniz. Böylece, coroutine’lerin farklı iş parçacıklarında çalışmasını sağlayabilir, hataları güvenli bir şekilde yakalayabilir ve coroutine’lerin yaşam döngüsünü yönetebilirsiniz.

Coroutine builder’lar

Coroutine builder’lar, coroutine’leri başlatmak için kullanılır ve üç ana türü vardır: launch, async, ve runBlocking. Bu builder'ların her birinin kendine özgü kullanım senaryoları ve özellikleri vardır.

launch, coroutine'leri başlatmak için kullanılan en yaygın builder'dır. Bir coroutine başlatır ve bu coroutine'in sonucu ile ilgilenmez. Genellikle yan etkileri olan işlemler için kullanılır.

async, bir coroutine başlatır ve bir Deferred sonucu döner. Bu sonuç daha sonra await fonksiyonu ile alınabilir. async genellikle birden fazla asenkron işlemi paralel olarak başlatmak için kullanılır.

Deferred, Kotlin Coroutines kütüphanesinde kullanılan bir arayüzdür ve asenkron bir işlemin sonucunu temsil eder. await() fonksiyonu, Deferred nesnesinin sonucunu elde etmek için bekler ve bu işlem tamamlandığında sonucu döner. Eğer coroutine halen çalışıyorsa, await() fonksiyonu coroutine'in tamamlanmasını bekler.

runBlocking,genellikle testler ve main fonksiyonlar için kullanılır, çünkü ana thread'i veya çağıran thread'i bloklar.

withContext fonksiyonu, belirli bir coroutine bağlamında bir kod bloğunu çalıştırmak için kullanılır. Ayrıca withContext ile herhangi bir scope içinde veya suspend fonksiyon içinde thread değiştirilebilir ve değiştirilen thread ile devam edilebilir.

Error Handling ve Exception Management

Kotlin Coroutines kullanırken, hata ve istisna yönetimi önemlidir. Coroutines ile çalışırken, hataları ve istisnaları yönetmek için çeşitli mekanizmalar kullanılır. Coroutine exception handling, SupervisorJob ve CoroutineExceptionHandler kullanımını kod örnekleriyle ele alacağız.

Temel Exception Handling

Coroutine içindeki istisnaları ele almak için try-catch blokları kullanılabilir.

SupervisorJob Kullanımı

SupervisorJob, coroutines içinde bir hiyerarşi oluşturarak, alt coroutine'lerden biri başarısız olduğunda üst coroutine'in etkilenmesini önler. Aşağıdaki örnekte, ilk coroutine başarısız olduğunda, ikinci coroutine SupervisorJob sayesinde hala çalışmaya devam eder.

CoroutineExceptionHandler Kullanımı

CoroutineExceptionHandler, coroutine içinde oluşan istisnaları ortak bir şekilde ele almak için kullanılır. Bu handler, bir coroutine'in başlatılmasında kullanılır ve coroutine içindeki istisnaları yakalar.

Android-Kotlin Coroutine yazımın sonuna geldik umarım hem öğretici hemde keyifli bir okuma olmuştur. Bir sonraki yazıda görüşmek üzere👋

Kaynaklar:

https://chatgpt.com

--

--