1

Below is my use case. I have a function fun requestFocus this function in turn call function fun configure which depends on a callback from system hence this function configure uses coundownlatch with count 1 and await until it is reset to zero when callback is received. For this I have marked requestFocus suspend and use Dispatchers.IO to do all its operation. Now there are multiple caller of requestFocus for ex function fun accept. The function accept does many things and all of them happens on same thread. function accept can also be called from main thread or intent service as well. My problem is since function configure is blocking I do not want to block main thread. Currently accept function looks like this

fun accept() { //do something requestFocus() // do something } 

I am not sure how I can call requestFocus from accept and have all the operation that happens post requestFocus execution to happen in same way. What I have done currently in accept function is below

fun accept() { //do something runBlocking{ requestFocus() // do something } 

But this creates problem as main thread gets blocked. Any suggestion what can I try? I have looked into documentation of Global scope and main scope.

1
  • Never use runBlocking except in a unit test. It otherwise completely defeats the purpose of coroutines. Use lifecycleScope.launch to start a coroutine. This is sort of like posting a Runnable to the main thread, but the code within the lambda can suspend and therefore call suspend functions. So if accept() represents a complete flow of logic, use fun accept() = lifecycleScope.launch { /*...*/ }. Commented Mar 16, 2020 at 16:49

1 Answer 1

2

You're looking for withContext block. withContext behaves similar to runBlocking but it suspends the thread instead of blocking it.

suspend fun accept() { //do something on calling thread withContext(Dispatchers.IO){ // Changes to Dispatchers.IO and suspends the calling thread requestFocus() // Calls requestFocus() on Dispatchers.IO // do something on Dispatchers.IO } // Resumes calling thread } 

You need to call accept from a coroutine scope or from another suspending function. Or you can create a coroutine with launch to start a coroutine:

fun accept() = launch(Dispatchers.Main) { // Starts a coroutine on the main thread //do something on main thread withContext(Dispatchers.IO){ // Changes to Dispatchers.IO and suspends the main thread requestFocus() // Calls requestFocus() on Dispatchers.IO // do something on Dispatchers.IO } // Resumes main thread } 
Sign up to request clarification or add additional context in comments.

4 Comments

Thanks for your help. It made things bit more clear to me. However as I said accept can be called from main and as well as background thread. So is it possible to do launch(inside accept) on the thread accept was called? Like by your second solution it will always be main thread. So just curious if its possible in coroutine
It is possible but if you want a sequencial-like execution then using launch won't work. When you create a coroutine with launch, you're creating a subprocess and the thread that creates that coroutine won't be suspended, so it will continue its execution so accept might finish before the coroutine you created inside it finishes. My second solution creates a coroutine and starts running on the main thread but if you remove Dispatchers.Main it will create a coroutine and start running on whatever thread you call accept.
Thanks. Just one more thing, Like I said I am using coundownlatch and await inside configure method which is called by requestFocus. I want to know if its good approach as countdown will hold the thread and block it. Is there any better alternative in kotlin-coroutine. For ex I know coroutine has delay instead of thread.sleep to work with coroutine, does it have similar thing for countdown use case?
You could replicate Countdownlatch behavior with coroutines. For example, create a lazy coroutine like this val job = launch(start = CoroutineStart.LAZY) {...} and start it with job.start() whenever you need. To verify that a coroutine is running you can do it like this if(job.isActive) {...} and wait for a coroutine to finish with job.join().

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.