Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can CoroutineScope(job+Dispatchers.Main) run on the main/UI thread?

If the operations inside CoroutineScope(job+Dispatchers.Main){...} run on the main thread then how come it does not violate Android's requirement that slow (blocking) operations (Networking etc.) are not allowed to run on the main/UI thread? I can run blocking operations with this scope and the UI does not freeze at all.

I would be grateful if someone could explain what is happening under the hood. My guess is that it is similar to how JavaScript manages blocking operations with the event loop, but I struggle to find any relevant materials.

like image 675
372 Avatar asked Feb 18 '20 03:02

372


2 Answers

My guess is that it is similar to how JavaScript manages blocking operations with the event loop

Yes, this is correct, the event loop is essential to making coroutines work. Basically, when you write this:

uiScope.launch {
    delay(1000)
    println("A second has passed")
}

it compiles into code that has the same effect as this:

Handler(Looper.mainLooper()).postDelayed(1000) { println("A second has passed") }

The main concept is the continuation, an object that implements a state machine that corresponds to the sequential code you wrote in a suspendable function. When you call delay or any other suspendable function, the continuation's entry-point method returns a special COROUTINE_SUSPENDED value. Later on, when some outside code comes up with the return value of the suspendable function, it must call continuation.resume(result). This call will be intercepted by the dispatcher in charge, which will post this call as an event on the GUI event loop. When the event handler is dequeued and executed, you are back inside the state machine which figures out where to resume the execution.

You can review this answer for a more fleshed-out example of using the Continuation API.

like image 63
Marko Topolnik Avatar answered Oct 31 '22 15:10

Marko Topolnik


Running blocking operations and running suspending operations on CoroutineScope(Dispatchers.Main) are two different things.

delay() is a suspending function and it is non blocking

CoroutineScope(Dispatchers.Main){
    delay(6000)
}

While Thread.sleep() is blocking and calling code below will cause ANR

CoroutineScope(Dispatchers.Main){
    Thread.sleep(6000)
}

I suggest you checking Kotlin coroutines talk by Roman Elizarov on Kotlinconf 2017, especially the part where he runs 100,000 delay()

like image 29
Dmitrii Leonov Avatar answered Oct 31 '22 15:10

Dmitrii Leonov