I came across some unexpected behavior while using coroutines in Android app.
Suppose I've got the following function, which is not "suspend". It starts worker threads and should block the calling thread until all workers terminate:
fun doSomething() : Result {
// producers init thread
Thread {
for (i in 0 until NUM_OF_MESSAGES) {
startNewProducer(i) // each producer is a thread
}
}.start()
// consumers init thread
Thread {
for (i in 0 until NUM_OF_MESSAGES) {
startNewConsumer() // each consumer is a thread
}
}.start()
synchronized(lock) {
while (numOfFinishedConsumers < NUM_OF_MESSAGES) {
try {
(lock as java.lang.Object).wait()
} catch (e: InterruptedException) {
return@synchronized
}
}
}
synchronized(lock) {
return Result(
System.currentTimeMillis() - startTimestamp,
numOfReceivedMessages
)
}
}
I know that (lock as java.lang.Object).wait()
is ugly and I'd be better off with ReentrantLock, but I intentionally wanted to fall to the most primitive level.
Now, if I execute this function without coroutine from Android's main thread, it blocks the calling thread (expected behavior):
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
someObject.doSomething()
}
However, if I just wrap it in a coroutine which is also executed on main thread, main thread isn't blocked anymore, but the functionality remains the same:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
CoroutineScope(Dispatchers.Main).launch {
val result = someObject.doSomething()
}
}
Two questions:
(lock as java.lang.Object).wait()
should've blocked the main thread. How comes it doesn't when coroutine is involved? Do coroutines have a mean of "intercepting" such low level interactions?Thanks
Like post()
on a View
, launch()
(usually) schedules work to be performed asynchronously with respect to the current bit of execution. So, the code in your lambda expression passed to launch()
will be run on the main application thread eventually, just as the Runnable
that you supply to post()
will be run on the main application thread eventually. However, your onCreate()
function will continue past the point of launch()
to do whatever else it is supposed to.
However, just like a Runnable
passed to post()
could still tie up the main application thread due to what work it does in run()
, your coroutine could still tie up the main application thread. It's just that this work would happen later than it would if you did the work directly in onCreate()
.
It's just that animations still work
IIRC, on newer versions of Android, animations themselves get processed on a separate "render" thread.
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