runBlocking — Kotlin'de, iletilen koroutin tamamlanana kadar mevcut iş parçacığını bloke eden bir Coroutine Builder'dır. launch ve async'in aksine, bir suspend fonksiyonu değildir ve normal (bloklayıcı) koddan çağrılabilir. JetBrains belgeleri, 2024'ne göre, runBlocking senkron ve asenkron dünyalar arasında bir köprü görevi görerek main fonksiyonundan ve testlerden koroutin başlatılmasına olanak tanır.
Ana Noktalar
runBlocking, yeni bir CoroutineScope oluşturan ve iletilen koroutini çalıştıran, tamamen bitene kadar mevcut iş parçacığını bloke eden bir Kotlin fonksiyonudur. Diğer tüm Coroutine Builder'ların aksine, runBlocking bir suspend fonksiyonu değildir ve sıradan senkron koddan çağrılabilir. runBlocking'in imzası bir CoroutineContext ve bir suspend bloğu alır, T türünde bir sonuç döndürür.
public fun <T> runBlocking(
context: CoroutineContext = EmptyCoroutineContext,
block: suspend CoroutineScope.() -> T
): T
runBlocking, mevcut iş parçacığında yeni bir event-loop başlatır. Koroutin bir suspend fonksiyonunu (örneğin, delay() veya await()) çağırdığında, runBlocking iş parçacığını bloke eder ve askıya alınan koroutin devam edene kadar aynı iş parçacığında diğer planlanmış koroutinleri çalıştırır. Bu işbirlikçi bloklamadır — iş parçacığı boşta değil, diğer koroutinleri işler.
runBlocking'in iç mekanizması bir event-loop'a dayanır: bir suspend fonksiyonu çağrıldığında, runBlocking mevcut bloğun yürütülmesini duraklatır ve kuyruktan diğer koroutinleri çalıştırır. Suspend fonksiyonu tamamlandığında, yürütme devam eder. Bu döngü tüm koroutinler bitene kadar devam eder.
runBlocking, koroutinleri yürütmek için kendi tek iş parçacıklı havuzunu kullanır. Dispatchers.IO veya Default'un aksine, runBlocking iş parçacıklarını değiştirmez — tüm koroutinleri mevcut iş parçacığında işler, yürütmelerini iç içe geçirir. Bu, aynı iş parçacığında yürütmeyi garanti eden tek oluşturucudur.
fun main() {
val threadName = Thread.currentThread().getName()
println("runBlocking öncesi $threadName üzerinde")
val result = runBlocking {
println("runBlocking içinde ${Thread.currentThread().getName()} üzerinde")
delay(500L)
"Done"
}
println("runBlocking sonrası: $result")
}
Çıktı, her üç println ifadesinin de aynı iş parçacığında yürütüldüğünü gösterecektir. runBlocking iş parçacıklarını değiştirmez, bir event-loop kullanarak tek bir iş parçacığı içinde işbirlikçi çoklu görev düzenler.
runBlocking üç senaryoda haklı çıkar: konsol uygulamalarında main() giriş noktası, suspend fonksiyonlarının birim testleri ve köprü — callback tabanlı veya bloklayıcı kütüphanelerden suspend kod çağrısı. Üretim Android kodunda, ana iş parçacığında kullanımı kesinlikle yasaktır.
| Senaryo | Uygulanabilirlik | Riskler |
|---|---|---|
| main() konsol uygulamasının | Evet | Yok — giriş noktasıdır, iş parçacığı UI'ı bloke etmez |
| JUnit testleri | Evet | Minimal — testler tanım gereği senkrondur |
| Android UI iş parçacığı | Hayır | ANR, gecikmeler, arayüz donması |
| Callback → Coroutine | Evet, dikkatle | Uzun işlemlerde iş parçacığı havuzu blokajı |
Android testleri için, runBlocking yerine kotlinx-coroutines-test ile TestDispatcher kullanın. Bu, zaman kontrolü, otomatik temizlik ve test izolasyonu sağlar.
Çoğu senaryoda, runBlocking asenkron alternatiflerle değiştirilebilir ve değiştirilmelidir. Android için bunlar viewModelScope, lifecycleScope veya uygun dispatcher ile CoroutineScope'dur. Testler için — TestCoroutineDispatcher ve runTest.
// Kötü: Android ana iş parçacığında runBlocking
runBlocking(Dispatchers.Main) {
val result = networkApi.fetchData()
textView.setText(result)
}
// İyi: lifecycleScope
lifecycleScope.launch {
val result = withContext(Dispatchers.IO) { networkApi.fetchData() }
textView.setText(result)
}
Testler için alternatif, kotlinx-coroutines-test kütüphanesinden runTest'dir. Sanal zamanlı bir TestCoroutineScope oluşturur, gerçek bekleme olmadan gecikmeleri test etmeye olanak tanır. Bu, testleri hızlandırır ve belirleyici hale getirir.
En yaygın senaryo, suspend fonksiyonlarını test etmektir. Testlerde runBlocking, mimariyi değiştirmeden bir koroutin sonucunu senkron olarak beklemenizi sağlar. İkinci senaryo, callback API'lerine sahip kütüphanelerdir; burada suspend fonksiyonları, runBlocking aracılığıyla bloklayıcı bir bağlamdan çağrılır.
// runBlocking ile suspend fonksiyonunu test et
class RepositoryTest {
@Test
fun `fetchUser returns correct data`() {
val repository = UserRepository(FakeApi())
val result = runBlocking {
repository.fetchUser("123")
}
assertEquals("John", result.name)
assertEquals("john@test.com", result.email)
}
}
Callback ve suspend dünyaları arasındaki köprü için, callbacks yerine runBlocking ile birlikte CompletableDeferred kullanın — bu, asenkron işlem zincirlerini basitleştirir ve kod okunabilirliğini artırır.
runBlocking'in yanlış kullanımı, bloklayıcı yaklaşımdan koroutinlere geçiş yaparken sık yapılan hatalardan biridir. Ana sorunlar: Android ana iş parçacığında çağrı, runBlocking'in iç içe geçirilmesi, asenkron fonksiyonlar içinde kullanımı ve runBlocking aracılığıyla uzun işlemlerin başlatılması.
Altın kural: runBlocking bir köprüdür, bir yedek değildir. Yalnızca bloklayıcı ve bloklayıcı olmayan dünyaları birbirine bağlamak için kullanın. Diğer tüm görevler için launch, async veya lifecycleScope kullanın.
Sıkça Sorulan Sorular
runBlocking, suspend fonksiyonu olmayan tek oluşturucudur. Mevcut iş parçacığında bir event-loop başlatır ve tüm koroutinler tamamlanana kadar kontrolü geri vermez. launch ve async hemen kontrolü geri verir, koroutini arka planda çalıştırır.
Önerilmez. ViewModel, koroutinleri otomatik olarak yöneten ve yok edildiğinde iptal eden yerleşik bir viewModelScope'a sahiptir. ViewModel'de runBlocking iş parçacığını bloke eder ve yaşam döngüsü iptaline yanıt vermez.
kotlinx-coroutines-test kütüphanesinden runTest kullanın. Sanal zaman kontrolü, otomatik iptal ve belirleyici yürütme ile TestCoroutineScope sağlar.
Event-loop, runBlocking içindeki bir olay işleme döngüsüdür. Bir koroutin askıya alındığında (ör: delay()), event-loop aynı iş parçacığında diğer hazır koroutinleri çalıştırmaya geçer. Bu, iş parçacığı değiştirmeden çoklu görev yanılsaması yaratır.
Aynı iş parçacığında iç içe runBlocking bir deadlock yaratır — dış blok içtekini bekler, ancak içteki dıştaki bitene kadar başlayamaz. Farklı iş parçacıklarında izin verilir ancak hata ayıklama karmaşıklığı nedeniyle kesinlikle önerilmez.
Özet
Anahtar teslim bir mobil uygulama geliştireceğiz
IT Sectr, 2017'den beri girişimler ve işletmeler için iOS ve Android uygulamaları oluşturmaktadır. Size danışmanlık yapacak ve en iyi çözümü önereceğiz.
Ayrıca okuyun