3

I am new to multithreading and looking for solution for this problem. I am launching a method in coroutine which updates data in my database and if it is updated I would like to update the UI for users. How to this? I cannot put runOnUiThread inside a coroutine. Is there some type of magic like -> when coroutine finished -> then -> runOnUi?

Greetings

CallMePedro
  • 51
  • 3
  • 19
  • Maybe watch the database for updates and re render the UI after an update is made. Room with LiveData can provide you observables for the same thing – NIKHIL MAURYA Dec 02 '19 at 10:38
  • Hmm thats a good idea. Will check this :) But is there a solution for await till something what is runing under a coroutine scope is finished? – CallMePedro Dec 02 '19 at 10:41
  • Any suspending function without async keyword will run sequentially in a scope so you can put two functions one to write and other to read from DB. – NIKHIL MAURYA Dec 02 '19 at 10:50
  • 1
    Take a look at this https://medium.com/androiddevelopers/suspending-over-views-example-260ce3dc9100 article here the animations on a view are done sequentially as well as simultaneously using coroutines(Edited the link) – NIKHIL MAURYA Dec 02 '19 at 10:52
  • Thank you :) Such a fresh news (written today :D) – CallMePedro Dec 02 '19 at 10:55
  • Even though this article doesn't provide you exactly what you want, it would encourage you to think about using coroutines more in your code and replacing the callbacks. – NIKHIL MAURYA Dec 02 '19 at 10:57

3 Answers3

4

You don't need to call runOnUiThread as the coroutine will have the main dispatcher as the context.

Let's say you have this helper function to offload work to the I/O thread.

suspend fun <T> withIO(block: suspend CoroutineScope.() -> T) = withContext(Dispatchers.IO, block)

If you are using a ViewModel, then you can call it like this

viewModelScope.launch {
    val result = withIO { 
        // You are on IO thread here.
        update your database
    }
    // The block will be suspended until the above task is done.
    // You are on UI thread now.
    // Update your UI.
}

If you are not using a ViewModel, you can also use

withContext(Disptachers.Main) {
   val result = withIO {
       // You are on IO thread
   }
   // You are back on the main thread with the result from the task
}
Froyo
  • 17,947
  • 8
  • 45
  • 73
1

Coroutine are task that work on different thread. What you really want is wating for changes in database. Coroutine in this idea could work for insert data in db, but listening part is role of ViewModel pattern.

I recently answer similar question to yours:

AutocompleteTextView with room

More specific could be this answer from another user:

Wait until Kotlin coroutine finishes in onCreateView()

Dak28
  • 259
  • 1
  • 8
1

So the basic problem is to jumping back to main thread after co-routine finishes

this can be done multiple ways

using launch(Dispatcher.Main)

from main thread init co-routine something like this

//launches coroutine running on main thread
GlobalScope.launch(Dispatchers.Main) {
    updateDb()
}

suspend fun updateDb(){
    //runs on worker thread and returns data
    val value = withContext(Dispatchers.IO){
       saveDataInDb();
    }
    //runs back on main thread
    updateUI(value);
}

However global scope should not be used You can read about that here https://medium.com/@elizarov/the-reason-to-avoid-globalscope-835337445abc

using async await

suspend fun saveInDb() {
    val value = GlobalScope.async {
        delay(1000)
        println("thread running on [${Thread.currentThread().name}]")
        10
    }
    println("value =  ${value.await()} thread running on [${Thread.currentThread().name}]")
}

output:

 thread running on [DefaultDispatcher-worker-1]
 value =  10 thread running on [main]
 thread running on [main]
Tushar Saha
  • 1,978
  • 24
  • 29