runBlocking — Coroutine Builder sa Kotlin na humaharang sa kasalukuyang thread hanggang sa matapos ang ibinigay na coroutine. Hindi tulad ng launch at async, hindi ito suspend-function at maaaring tawagin mula sa ordinaryong (blocking) code. Ayon sa dokumentasyon ng JetBrains, 2024, ang runBlocking ay nagsisilbing tulay sa pagitan ng synchronous at asynchronous na mundo, na nagpapahintulot sa pagpapatakbo ng mga coroutine mula sa main-function at mga pagsubok.
Mga Pangunahing Punto
runBlocking — isang function ng Kotlin na lumilikha ng bagong CoroutineScope at nagpapatakbo ng ibinigay na coroutine, hinaharangan ang kasalukuyang thread hanggang sa ganap itong makumpleto. Hindi tulad ng lahat ng iba pang Coroutine Builder, ang runBlocking ay hindi suspend-function at maaaring tawagin mula sa ordinaryong synchronous code. Ang signature ng runBlocking ay tumatanggap ng CoroutineContext at suspend-block, nagbabalik ng resulta ng uri T.
public fun <T> runBlocking(
context: CoroutineContext = EmptyCoroutineContext,
block: suspend CoroutineScope.() -> T
): T
Ang runBlocking ay nagpapasimula ng bagong event-loop sa kasalukuyang thread. Kapag ang coroutine ay tumawag ng suspend-function (hal. delay() o await()), hinaharangan ng runBlocking ang thread at pinapatakbo ang iba pang naka-iskedyul na coroutine sa parehong thread hanggang sa ipagpatuloy ang nasuspinde. Ito ay cooperative blocking — ang thread ay hindi nakatigil, kundi nagpoproseso ng iba pang coroutine.
Ang panloob na mekanismo ng runBlocking ay batay sa event-loop: kapag tumawag ng suspend-function, sinuspinde ng runBlocking ang pagpapatupad ng kasalukuyang bloke at pinapatakbo ang iba pang coroutine mula sa pila. Kapag natapos na ang suspend-function, ipinagpapatuloy ang pagpapatupad. Ang siklong ito ay nagpapatuloy hanggang sa matapos ang lahat ng coroutine.
Ang runBlocking ay gumagamit ng sarili nitong single-thread pool para mag-execute ng mga coroutine. Hindi tulad ng Dispatchers.IO o Default, ang runBlocking ay hindi nagpapalit ng thread — pinoproseso nito ang lahat ng coroutine sa kasalukuyang thread, pinagpapalit-palit ang pagpapatupad nito. Ito ang tanging builder na ginagarantiyahan ang pagpapatupad sa parehong thread.
fun main() {
val threadName = Thread.currentThread().getName()
println("Bago ang runBlocking sa $threadName")
val result = runBlocking {
println("Sa loob ng runBlocking sa ${Thread.currentThread().getName()}")
delay(500L)
"Done"
}
println("Pagkatapos ng runBlocking: $result")
}
Ang output ay magpapakita na lahat ng tatlong println ay na-execute sa isang thread. Ang runBlocking ay hindi nagpapalit ng thread, kundi nag-oorganisa ng cooperative multitasking sa loob ng isang thread sa pamamagitan ng event-loop.
Ang runBlocking ay makatwiran sa tatlong sitwasyon: entry point main() sa console applications, unit testing ng suspend-functions, at tulay — pagtawag ng suspend-code mula sa callback-based o blocking libraries. Sa Android production code, ang paggamit sa main thread ay mahigpit na ipinagbabawal.
| Sitwasyon | Pwedeng gamitin | Mga Panganib |
|---|---|---|
| main() console application | Oo | Wala — ito ang entry point, hindi hinaharangan ng thread ang UI |
| JUnit tests | Oo | Minimal — ang mga test ay synchronous ayon sa depinisyon |
| Android UI-Thread | Hindi | ANR, pagkaantala, pag-freeze ng interface |
| Callback → Coroutine | Oo, nang may pag-iingat | Pagharang ng thread pool sa mahabang operasyon |
Para sa Android testing gamitin ang kotlinx-coroutines-test na may TestDispatcher sa halip na runBlocking. Ito ay nagbibigay ng kontrol sa oras, awtomatikong pag-reset, at paghihiwalay ng mga test.
Sa karamihan ng mga sitwasyon, ang runBlocking ay maaari at dapat palitan ng mga asynchronous na alternatibo. Para sa Android, ito ay viewModelScope, lifecycleScope, o CoroutineScope na may tamang dispatcher. Para sa testing — TestCoroutineDispatcher at runTest.
// Mali: runBlocking sa Android main thread
runBlocking(Dispatchers.Main) {
val result = networkApi.fetchData()
textView.setText(result)
}
// Tama: lifecycleScope
lifecycleScope.launch {
val result = withContext(Dispatchers.IO) { networkApi.fetchData() }
textView.setText(result)
}
Ang kapalit para sa testing ay runTest mula sa kotlinx-coroutines-test. Lumilikha ito ng TestCoroutineScope na may virtual na oras, na nagpapahintulot sa pag-test ng mga pagkaantala nang walang tunay na paghihintay. Pinapabilis nito ang mga test at ginagawa itong deterministiko.
Ang pinakakaraniwang sitwasyon — pag-test ng suspend-functions. Ang runBlocking sa mga test ay nagpapahintulot ng synchronous na paghihintay para sa resulta ng coroutine nang hindi binabago ang arkitektura. Ang pangalawang sitwasyon — mga library na may callback API, kung saan ang suspend-functions ay tinatawag mula sa blocking context sa pamamagitan ng runBlocking.
// Test suspend function gamit ang runBlocking
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)
}
}
Para sa tulay sa pagitan ng callback at suspend na mundo, gamitin ang CompletableDeferred sa kombinasyon ng runBlocking sa halip na mga callback — pinapasimple nito ang chain ng mga asynchronous na operasyon at pinapabuti ang pagiging madaling mabasa ng code.
Ang maling paggamit ng runBlocking ay isa sa mga karaniwang pagkakamali sa paglipat mula sa blocking approach patungo sa coroutines. Mga pangunahing problema: pagtawag sa Android Main-Thread, pagpupugad ng runBlocking, paggamit sa loob ng asynchronous functions, at pagpapatakbo ng mahabang operasyon sa pamamagitan ng runBlocking.
Gintong patakaran: ang runBlocking ay isang tulay, hindi kapalit. Gamitin lamang ito para sa pagkonekta ng blocking at non-blocking na mundo. Para sa lahat ng iba pang gawain, gamitin ang launch, async, o lifecycleScope.
Mga Madalas Itanong
runBlocking ay ang tanging builder na hindi suspend-function. Nagpapasimula ito ng event-loop sa kasalukuyang thread at hindi nagbabalik ng kontrol hanggang sa matapos ang lahat ng coroutine. Ang launch at async ay agad na nagbabalik ng kontrol, pinapatakbo ang coroutine sa background.
Hindi inirerekomenda. Ang ViewModel ay may built-in na viewModelScope na awtomatikong namamahala ng mga coroutine at kinakansela ang mga ito kapag nawasak. Ang runBlocking sa ViewModel ay humaharang sa thread at hindi tumutugon sa pagkansela ng lifecycle.
Gamitin ang runTest mula sa library na kotlinx-coroutines-test. Nagbibigay ito ng TestCoroutineScope na may kontrol ng virtual na oras, awtomatikong pagkansela, at deterministikong pagpapatupad.
Event-loop — ang siklo ng pagproseso ng mga kaganapan sa loob ng runBlocking. Kapag ang coroutine ay nasuspinde (hal. delay()), ang event-loop ay lumilipat sa pagpapatupad ng iba pang handa na coroutine sa parehong thread. Lumilikha ito ng ilusyon ng multitasking nang walang pagpapalit ng thread.
Nested runBlocking sa isang thread ay lumilikha ng deadlock — ang panlabas na block ay naghihintay sa panloob, ngunit ang panloob ay hindi maaaring magsimula hanggang sa matapos ang panlabas. Sa iba't ibang thread ito ay katanggap-tanggap, ngunit lubos na hindi inirerekomenda dahil sa kahirapan ng debugging.
Buod
Gagawa kami ng mobile application na turnkey
Gumagawa ang IT Sectr ng mga iOS at Android application para sa mga startup at negosyo mula noong 2017. Magpapayo kami sa iyo at magmumungkahi ng pinakamahusay na solusyon.
Basahin din