5 Nisan 2024·4 dk okuma
Kotlin Coroutines Üzerine

Neden Coroutines?
Asenkron programlama, kullanıcı arayüzünü bloklamadan uzun süreli işlemleri gerçekleştirmemizi sağlar. Android uygulamalarında, ağ istekleri veya veri tabanı işlemleri gibi uzun sürebilecek işlemleri asenkron olarak yapmamız gerekir. Callback’ler ve RxJava gibi diğer asenkron programlama yöntemleri genellikle karmaşık ve okunması zor kodlara yol açabilir. Kotlin Coroutines, bu sorunları çözmek için tasarlanmıştır ve basit, sade bir API ile güçlü bir asenkron programlama modeli sunar.
Coroutine Temelleri
Coroutines, askıya alınabilen ve devam ettirilebilen fonksiyonlar etrafında döner. suspend anahtar kelimesi, bir fonksiyonun askıya alınabileceğini ve daha sonra devam ettirilebileceğini belirtir.
Basit bir örnekle başlayalım:
import kotlinx.coroutines.*fun main() = runBlocking {
launch {
delay(1000L)
println("Dünya")
}
println("Merhaba")
}Bu kodun çıktısı sırasıyla “Merhaba” ve “Dünya” olacaktır. runBlocking, bir coroutine başlatır ve içindeki tüm coroutine’ler tamamlanana kadar bloklanır. launch , yeni bir coroutine başlatır. , coroutine’i belirtilen süre boyunca askıya alır.
CoroutineScope ve Coroutine Builder’lar
Coroutines, belirli bir scope içinde çalıştırılır. CoroutineScope ile coroutine’leri başlatır ve yönetiriz. En yaygın kullanılan coroutine builder’lar şunlardır:
launch: Bir coroutine başlatır ve Job döner.
async: Bir coroutine başlatır ve Deferred döner, await ile sonucunu alabiliriz.
runBlocking: Mevcut thread’i bloklayarak bir coroutine başlatır.
Örnek:
import kotlinx.coroutines.*fun main() = runBlocking {
val job = launch {
delay(1000L)
println("Coroutine tamamlandı.")
}
println("Main thread çalışıyor.")
job.join() // Coroutine tamamlanana kadar bekle
println("Main thread tamamlandı.")
}Suspend Fonksiyonlar
Suspend fonksiyonlar, coroutine içinde kullanılabilen ve başka bir coroutine’i askıya alabilen fonksiyonlardır. Bu, uzun sürebilecek işlemleri kolayca yönetmemizi sağlar.
Örnek:
suspend fun fetchData(): String {
delay(1000L) // Simulated network request
return "Data fetched"
}fun main() = runBlocking {
val data = fetchData()
println(data)
}Burada fetchData fonksiyonu, bir ağ isteği simülasyonu yapar ve 1 saniye bekledikten sonra bir veri döner.
Dispatchers ile Çalışmak
Coroutines, farklı thread’lerde çalıştırılabilir. Dispatchers ile hangi thread’de çalışacağını belirtebiliriz:
Dispatchers.Default: CPU yoğun işler için kullanılır.
Dispatchers.IO: Giriş/Çıkış işlemleri için kullanılır.
Dispatchers.Main: Ana thread’de çalışmak için kullanılır (genellikle Android’de kullanılır).
Örnek:
import kotlinx.coroutines.*fun main() = runBlocking {
launch(Dispatchers.Default) {
println("Default Dispatcher: ${Thread.currentThread().name}")
}
launch(Dispatchers.IO) {
println("IO Dispatcher: ${Thread.currentThread().name}")
}
launch(Dispatchers.Main) {
println("Main Dispatcher: ${Thread.currentThread().name}")
}
}CoroutineScope ve SupervisorScope
CoroutineScope, coroutine’leri yönetmek için kullanılır. launch ve async gibi coroutine başlatan fonksiyonlar, belirli bir scope içinde çalıştırılır. SupervisorScope ise hata yönetimi için kullanışlıdır.
Örnek:
fun main() = runBlocking {
val scope = CoroutineScope(Dispatchers.Default)
scope.launch {
try {
val data = fetchData()
println(data)
} catch (e: Exception) {
println("Error: ${e.message}")
}
}
println("Fetching data...")
}CoroutineScope ile coroutine’leri başlatıp yönetebiliriz. Eğer bir coroutine hata verirse, diğer coroutine’ler etkilenmez ve çalışmaya devam eder.
Structured Concurrency (Yapısal Eşzamanlılık)
Coroutines, yapısal eşzamanlılık ilkesine dayanır. Bu, coroutine’lerin bir hiyerarşi içinde düzenlendiği ve üst coroutine’in alt coroutine’lerin tamamlanmasını beklediği anlamına gelir.
Örnek:
fun main() = runBlocking {
launch {
supervisorScope {
val job1 = launch {
delay(1000L)
println("Job 1 tamamlandı.")
}
val job2 = launch {
delay(500L)
println("Job 2 tamamlandı.")
}
}
println("Supervisor scope tamamlandı.")
}
println("Main thread çalışıyor.")
}Burada supervisorScope kullanarak, bir coroutine’de hata oluştuğunda diğer coroutine’lerin etkilenmemesini sağlıyoruz.
Flow API
Flow, Kotlin Coroutines içinde yer alan bir veri akışı yapısıdır ve reaktif programlama için kullanılır. Flow, veri akışlarını yönetmek ve işlemek için güçlü bir araçtır.
Örnek:
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*fun simpleFlow(): Flow<Int> = flow {
for (i in 1..3) {
delay(100L)
emit(i)
}
}fun main() = runBlocking {
simpleFlow().collect { value ->
println(value)
}
}Bu örnekte, simpleFlow fonksiyonu bir Flow oluşturur ve 1, 2, 3 değerlerini 100 milisaniye aralıklarla yayar.
Android Uygulamalarında Coroutines Kullanımı
Android geliştirmede, Coroutines kullanarak asenkron işlemleri yönetmek oldukça yaygındır. Aşağıda, bir ağ isteği ve veri tabanı işlemi gibi yaygın senaryolar için Coroutine kullanımına örnekler verelim.
Ağ İsteği:
import kotlinx.coroutines.*
import okhttp3.OkHttpClient
import okhttp3.Requestsuspend fun fetchApiData(): String {
val client = OkHttpClient()
val request = Request.Builder().url("https://api.example.com/data").build()
return withContext(Dispatchers.IO) {
client.newCall(request).execute().body?.string() ?: "Error"
}
}fun main() = runBlocking {
val data = fetchApiData()
println(data)
}Veri Tabanı İşlemi:
import kotlinx.coroutines.*
import androidx.room.*@Entity
data class User(
@PrimaryKey val id: Int,
val name: String
)@Dao
interface UserDao {
@Query("SELECT * FROM user")
suspend fun getAll(): List<User> @Insert
suspend fun insertAll(vararg users: User)
}@Database(entities = [User::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
abstract fun userDao(): UserDao
}fun main() = runBlocking {
val db = Room.databaseBuilder(
applicationContext,
AppDatabase::class.java, "database-name"
).build() val userDao = db.userDao() launch(Dispatchers.IO) {
val users = userDao.getAll()
println(users)
}
}Kotlin Coroutines, asenkron programlamayı daha kolay, okunabilir ve performanslı hale getirir. suspend fonksiyonlar, Dispatchers, CoroutineScope, Flow gibi kavramlar ile güçlü ve esnek bir yapı sunar. Asenkron işlemleri yönetmek ve kullanıcı deneyimini artırmak için Coroutines, modern Android geliştirmenin vazgeçilmez bir parçasıdır.
Umarım bu yazım, Kotlin Coroutines hakkında daha iyi bir anlayış sağlar. İyi kodlamalar!

Yazan
Serhat TaştanMobile & Web Developer & Trainer & CATH Lab Nurse
Mühendislik disiplinine sahip bir yazılım geliştirici ve Başakşehir Çam ve Sakura Şehir Hastanesi'nde (Anjiyo Ünitesi) görev yapan bir hemşireyim. Unvanlara takılmadan, klinik deneyimimi ve yazılım yeteneklerimi birleştirerek problem odaklı çözümler üretiyorum. Çok yönlülüğün gücüne inanarak, ister hastanede ister klavye başında süreçleri iyileştiriyor ve somut sonuçlar yaratıyorum.