Android -Kotlin Coroutines
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.
- Coroutine Dispatcher:
suspend
fonksiyonlar belirli bir dispatcher üzerinde çalışır. Dispatchers, coroutine'lerin hangi thread üzerinde çalışacağını belirler. - 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. - Suspension Point:
delay
gibi birsuspend
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. - 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 blokcreated
durumunda tekrar eder vedestroyed
olduğ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 blokstarted
durumunda tekrar eder vestopped
olduğ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 blokresumed
tekrar eder vepaused
olduğ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👋