Please help me in understanding this scenario. Consider following code.
val scope = MainScope()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
runBlocking {
val myJob = scope.launch {
println("Launching coroutine")
}
myJob.join()
}
}
It prints nothing. If I change the dispatcher like
val scope = MainScope() + Dispatchers.Default
OR did not call join() method then it works i-e prints Launching coroutine
Thank you.
1 Like
The problem is that you are deadlocking on the main thread here.
Always remember that runBlocking
blocks the current thread while its body is executing, so you need to use it with lots of care. Let’s break down what happens in your case:
-
onCreate()
is called on the main thread
-
runBlocking
blocks the current thread (the main thread) to run the lambda
-
scope.launch{}
launches a coroutine in scope
(which is MainScope()
) so it uses Dispatchers.Main
, which in turn needs the main thread. It is therefore kinda “enqueued” (in the Main
dispatcher) and waits for the main thread to be free so it can run
-
myJob.join()
suspends until the launched coroutine is complete, which means runBlocking
continues blocking the main thread to wait for it
In this situation, the launched coroutine waits for the main thread to be free, and the runBlocking
waits for the coroutine to be complete, and blocks the main thread while doing so. The main thread is therefore never free. Deadlock.
Note that using an external scope
to run stuff while inside runBlocking
is quite confusing, because coroutine builders like runBlocking
already provide a scope to you as this
in the lambda. By not using it, you’re effectively running your other coroutine completely independently from runBlocking
, without structured concurrency. This is rather bad practice.
Now, what exactly are you trying to do here? If you launch
and immediately join
the job, you may as well just write the stuff that’s inside the launch directly in the runBlocking
body instead:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
runBlocking {
println("Launching coroutine") // assuming you run suspending stuff here, otherwise no need for `runBlocking`
}
}
4 Likes
@joffrey.bion Thank you for the detail explanation.