This code uses the old Coroutines Api. If you're using kotlinx-coroutines 1.1.0 or newer, this code won't be useful to you
The original question was:
I'm finding that this particular code in my Android App blocks the UI Thread:
runBlocking {
async(CommonPool) {
Thread.sleep(5000)
}.await()
}
textView.text = "Finish!"
I've been using coroutines for several tasks, and they never block UI Thread, as can be read in the documentation:
. Coroutines provide a way to avoid blocking a thread and replace it with a cheaper and more controllable operation: suspension of a coroutine
But curiously, this code:
runBlocking {
async(CommonPool) {
launch(CommonPool) {
Thread.sleep(5000)
runOnUiThread { textView.text = "Finish!" }
}
}.await()
}
behaves as expected; does not block, waits five seconds then prints the result (I need to update the UI after, and only after the sleep
is completed)
The documentation says that async
and launch
can be used independently and don't need to be combined. In fact, async(CommonPool)
should be enough.
So what's really going on here? why does it work only with async+launch
?
[Deprecation alert] This code uses the old Coroutines Api. If you're using kotlinx-coroutines 1.1.0 or newer, forget about this code
My full sample code:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
button1.setOnClickListener {
runBlocking {
async(CommonPool) {
Thread.sleep(5000L)
}.await()
}
textView1.text = "Finally! I've been blocked for 5s :-("
}
button2.setOnClickListener {
runBlocking {
async(CommonPool) {
launch(CommonPool) {
Thread.sleep(5000L)
runOnUiThread { textView1.text = "Done! UI was not blocked :-)" }
}
}.await()
}
}
}
}
Note: this post dates back to the pre-release version of coroutines. I updated the names of dispatchers to match the release version.
runBlocking
is not the way to start a coroutine on the UI thread because, as its name says, it will block the hosting thread until the coroutine is done. You have to launch
it in the Main
context and then switch to the Default
context for the heavyweight operation. You should also drop the async-await
pair and use withContext
:
button1.setOnClickListener {
launch(Main) {
withContext(Default) {
Thread.sleep(5000L)
}
textView1.text = "Done! UI was not blocked :-)"
}
}
withContext
will suspend the coroutine until done and then resume it in the parent context, which is Main
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With