Launching a Kotlin Coroutine for immediate execution on the Main thread
If you launch a coroutine using
launch(Dispatchers.Main) while already on the main thread, will the code execute immediately?
No, is the short answer. This post explains why.
Consider the following code. As this is the
onCreate function of an
Activity, we know it is running on the main thread.
We expect that two things will be printed to the logs; “A” and “B”. But in which order?
Does it surprise you that it prints “B” first? It surprised me.
Why This Happens
The answer lies here:
trueif execution shall be dispatched onto another thread. The default behaviour for most dispatchers is to return
OK, that makes sense. Each dispatcher can choose if execution should happen on another thread or not. And the default is for it to be
true. But what about a UI dispatcher like
Dispatchers.Main? Shouldn’t it return
false so that a coroutine is executed immediately if already on the main thread? Wouldn’t that be more efficient?
Turns out… yes, that would be more efficient but even so, no, it shouldn’t do that.
UI dispatchers should not override
isDispatchNeeded, but leave a default implementation that returns
Main dispatcher could check if it was already running on the main thread and, if it were, execute the code immediately instead of queuing it up. However, as the documentation for that method highlights, this could introduce pernicious bugs which are hard to debug.
If a background thread called the code, it would execute asynchronously, but if it were called on the main thread it would execute synchronously. That’s not going to be easy to debug if something goes wrong because of it.
If Not Immediately, When?
We can see it doesn’t execute immediately; so when exactly does it execute? Under the hood, the Main dispatcher uses a
Handler to post a
Runnable to the
MessageQueue. Basically, it’ll get added to the end of the event queue.
This means it will execute soon, but not immediately. Hence why “B” gets printed before “A”.
What if you do need it to execute immediately? For this, you can use
Executes coroutines immediately when it is already in the right context
In other words, if your code is called from the main thread and you use this dispatcher, it will execute immediately.
If this introduces subtle bugs into your code, well, you can’t blame coroutines for that. By making the default behaviour safe, they protect against accidentally introducing unexpected behaviour. If it’s really the behaviour you want though, you can have it, but you have to be explicit about it.
Dispatchers.Main.immediate is currently marked as experimental.
Big thanks to Jake Wharton for explaining the behaviour described above to me in the first place, and for proof reading this article. 👏