Reputation: 99
I would like to improve my async-await
in coroutines.
This is my solution
val coroutineContext: CoroutineContext
get() = job + Dispatchers.IO
[...]
lifecycleScope.launch(coroutineContext) {
async {
client = viewModel.getClientItem(clientId)
}.await()
if (inEditMode != null) {
changedEditMode = true
}
[...]
@Marko Topolnik wrote in Why does this coroutine block UI Thread?
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.
Why doing it with Main Dispatchers and then withContext using Default dispatchers is betters solution than async. It will also block main thread and work the same what is the difference. And how to handle this approaches?
Greetings
EDIT:
My other solution is this below or withContext(Dispatchers.IO)
lifecycleScope.launch(Dispatchers.Main) {
withContext(Dispatchers.Default) {
client = viewModel.getItem(clientId)
}
[...]
}
Now as I read I do not use runBlocking and don't block main thread? In that configuration it works the same as with async-await. In your opinin that's better solution ?
Upvotes: 2
Views: 1037
Reputation: 37849
Why doing it with Main Dispatchers and then withContext using Default dispatchers is betters solution than async. It will also block main thread and work the same what is the difference
withContext
doesn't block, it suspends. The terminology might should similar but the behaviour is very different. While Thread.sleep
is executing on the Default
dispatcher (and thus on one of this dispatcher's threads), the main thread can keep running other code. The coroutine launched via launch(Main)
here is in a suspended state, which means the main dispatcher knows it can execute other things instead. The linked answer here explains why runBlocking
was a bad idea, because no matter what you launch in it runBlocking
has to block the current thread while it's executing coroutines inside.
Now back to your code. Note that async { doStuff() }.await()
makes little sense. It is equivalent to simply doStuff()
.
Another thing is that you're passing a job
explicitly to launch
via the context here. This doesn't seem right, and could lead to problems. If you want to use Dispatchers.IO
, you don't need to pass a job
too.
That said, even Dispatchers.IO
seems like a stretch here. You should instead make viewModel.getClientItem
a suspend function (if not already), and leave the responsibility of using the right dispatcher to this function. Maybe you don't even need IO at all here, or maybe it's an HTTP or DB call that already has its own thread pool anyway.
So I would replace your whole code with:
lifecycleScope.launch {
client = viewModel.getClientItem(clientId)
if (inEditMode != null) {
hangedEditMode = true
}
}
Upvotes: 2