Android How to execute flow.map() in viewModel on Dispatchers.Default - android

I am developing an Android application, using the MVVM design pattern.
So, I have a repository that exposes a flow<T> to a ViewModel. The ViewModel then converts the flow<T> to LiveData<T> using asLiveData() as per the code below.
repository.getFlow().map {
// I will do some long running work here
}.asLiveData()
As you can see, I will do some long-running work in map { }. So I want to execute the code inside map { } in Dispatchers.Default thread.
From my research, I can do with flowOn(Dispatchers.Default) or asLiveData(viewmodelScope.coroutineContext + Dispatchers.Default) as per the code below.
map{} with flowOn(Dispatchers.Default)
repository.getFlow().map {
// I will do some long running work here
}.flowOn(Dispatchers.Default).asLiveData()
map{} with asLiveData(viewmodelScope.coroutineContext + Dispatchers.Default)
repository.getFlow().map {
// I will do some long running work here
}.asLiveData(viewmodelScope.coroutineContext + Dispatchers.Default)
I'd like to understand better about coroutine context, scope and flowOn(). So my questions are as follows
1-1. Does asLiveData(viewmodelScope.coroutineContext + Dispatchers.Default) mean that asLiveData() function will execute in a new coroutine with Dispatchers.Default in viewmodelScope?
1-2. So, then the map {} function executes in the new coroutine with Dispatchers.Default because flow's intermediate operator executes in consumer's coroutine?
I also read flowOn(Dispatchers.Default) executes the upstream flow in the CoroutineContext defined in flowOn() function, which mean that the map{} will execute in Dispatchers.Default thread. Is it fine to use flowOn() in viewmodel with asLiveData()?
Thank you guys

Not sure I understand the question, but basically it means that the flow collection will happen on the passed coroutine context (viewmodelScope + bg thread) and the value will be propagated to the livedata from the bg thread. But the propagation (an emit() call) will always switch to the Main dispatcher.
Yes, when you use flowOn(), all the upstream operators (your map) are run on the defined dispatcher until new flowOn() operator appears. And yes, it is ok to use it with combination with asLiveData().
These two options should be equivalent; I would aim for the clarity, which seems to be better covered with the flowOn() usage.

Related

How to understand and improve async-await in coroutines?

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
?
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
}
}

Using coroutines to access the database

So far I've been using this pattern whenever I want to access the database:
runBlocking {
launch {
// fetch something from the database and put it to some view
}
}
Now that I'm diving deeper into Kotlin coroutines, I'm increasingly convinced, that this is a bad pattern. Essentially, I might as well just allowMainThreadQueries, as my pattern blocks the main thread anyway.
Unfortunately, I haven't found a proper pattern yet. How to effectively use Kotlins coroutines to access the database?
Is runBlocking the only entry point into coroutines?
Consider this scenario:
override fun onCreate() {
setContentView(someLayout)
// concurrently fetch something from the database and put it in some view
// onCreate may return before this has finished
someButton.setOnClickListener {
// concurrently insert or update something in the database
}
}
You should never use runBlocking in an Android project, unless you are mixing Kotlin coroutines code with some Java code that can't use coroutines and it needs a way to call some coroutine in a blocking manner on one of its own background threads. In this case, you might use runBlocking to create a bridge function for the Java code to call, but you would never call this function from Kotlin and certainly never call it from the main thread. Calling blocking code on the main thread freezes the UI, which makes your app feel janky and risks triggering an ANR (application not responding) error.
The correct way to begin a coroutine is to use a CoroutineScope to launch your coroutine. These are already provided for you by the Android Jetpack framework for Activities, Fragments, and ViewModels.
In an Activity, use lifecycleScope.launch. In a Fragment, you should usually use viewLifecycleOwner.lifecycleScope.launch. In a ViewModel, use viewModelScope.launch.
What does using a CoroutineScope instead of runBlocking do? It prevents the long-running suspending actions (like reading the database from the disk) from blocking the main thread and freezing your UI. And it automatically cancels the long-running work when the Activity/Fragment/ViewModel is torn down, so it prevents memory leaks and wasted resources.
Assume that you are using Room,
runBlocking and allowMainThreadQueries are usually used for Test purpose and you should never use them in release product.
what allowMainThreadQueries do is give you permission to access database from Main Thread which is you should Never do, because it may freeze the UI.
use lifecycleScope.launch to launch coroutine from Fragment/Activity or viewModelScope.launch from ViewModel, you might need to explicitly add the dependencies
def lifecycleVersion = '2.4.0'
// ViewModel
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycleVersion"
// Lifecycles only (without ViewModel or LiveData)
implementation("androidx.lifecycle:lifecycle-runtime-ktx:$lifecycleVersion")
Lifecycle release note https://developer.android.com/jetpack/androidx/releases/lifecycle
you should call the database operation from the ViewModel to prevent cancelation from configuration change, If the user rotate the screen while the operation is in progress, it'll be canceled and the result won't be cached.
In Activity/Fragment
// read data from db
lifecycleScope.launch {
viewModel.someData.collect {
//do some stuff
}
}
// insert
someButton.setOnClickListener {
viewModel.insertToDatabase(someData)
}
In ViewModel
class MainViewModel (val dao: Dao) : ViewModel() {
// you should expose your data as Observable
val someData : Flow<List<SomeData>> = dao.getAllSomeData()
fun insertToDatabase(data:SomeData) = viewModelScope.launch {
dao.insert(data)
}
}
// query
#Query("SELECT * FROM some_data_table")
abstract fun getAllSomeData(): Flow<List<SomeData>>

Why use withContext() vs Async-await, isn't the goal of coroutines to achieve concurrency?

I am learning about threading and kotlin coroutines to load my UI and repository concurrently, but then "withContext()" as an alternative to async-await.
If I understood "withContext()" correctly and it executes one task after another waiting for the previous task to finish, why ever use it? Is there another concept I'm missing?
withContext is a suspending function that allows to execute a specific piece of code in a different coroutine context. It is in particular useful when you want to execute something in a different dispatcher.
For instance, you could have some code run in the default dispatcher with multiple threads, but then use a value produced by that code to update some UI in the UI thread. In that case, you're not looking for concurrency, the computation on the default dispatcher has to happen before updating the UI because you need the result:
val result = someComputation()
withContext(Dispatchers.Main) {
updateUI(result)
}
Of course, even if the computation and the update of the UI are not concurrent, their sequence can be concurrent with other pieces of code:
scope.launch(Dispatchers.Default) {
val result = someComputation()
withContext(Dispatchers.Main) {
updateUI(result)
}
}
If you need to execute concurrent things, you can use coroutine builders like launch and async. However, using async { ... } immediately followed by .await() defeats the purpose of concurrency, because the code between the async and await() calls is precisely what will run concurrently with the async's body:
val deferred = async { computeSomeValue() }
somethingConcurrentWithAsyncBody()
val result = deferred.await()
You can read more about how to organize your calls to achieve concurrency in the part of the doc about composing suspend functions.

Kotlin difference between CoroutineScope and withContext

To change the thread in a function I use either CoroutineScope or withContext. I don't know's the difference, but with CourineScope I can also use a handler.
Examples:
private fun removeViews(){
CoroutineScope(Main).launch(handler){
gridRoot.removeAllViews()
}
}
private suspend fun removeViews(){
withContext(Main){
gridRoot.removeAllViews()
}
}
I call this function from a coroutine that works on background thread (IO). Is any more appropriate than the other?
These two are actually radically different and you just happen to have a use case where you don't experience the difference:
CoroutineScope(Main).launch(handler){
This launches a concurrent coroutine that goes on independently.
withContext(Main){
This is a function that completes only when the code inside it completes, and returns its result. This is the way you should be doing it.
The first approach, with CoroutineScope, has another deficiency in that it circumvents structured concurrency. You create an ad-hoc coroutine scope that has no parent and thus won't be automatically cleaned up if it takes a longer time to complete and your GUI is dropped (user navigates away from the current Activity).
You should actually never use the CoroutineScope(Main) idiom, I don't think there's a single instance where it would be appropriate. If you explicitly want to avoid structured concurrency, it is still better and cleaner to write
GlobalScope.launch(Main + handler) {
and has pretty much the same effect.
If you want a concurrent coroutine that fits into structured concurrency, use
fun CoroutineScope.removeViews() {
launch {
gridRoot.removeAllViews()
}
}
Note I removed the handler argument, a child coroutine ignores it because it forwards any failures to its parent coroutine, which is exactly what you want. The parent coroutine should have an exception handler installed.
Technically both are same but when it comes to use case both are different and has big impact on the different use cases so be careful while using them
Coroutine Scope:
CoroutineScope is a starting Point of Coroutine. CoroutineScope can have more than one coroutine within itself, which makes coroutine hierarchy.
Lets think, Parent has more than one children. Think CoroutineScope is a parent and this parent can have more than one child which are also coroutines. These childrens are known as job
private val coroutineScope = CoroutineScope()
coroutineScope(IO).launch{
val childOne = launch(Main){}
val childTwo = launch(Main){}
}
see that childOne and childTwo? why we need these? because we can't directly cancel the coroutine there is no such way the coroutine can be cancelled directly, either the coroutine gets completed or it gets failed. But what if we wanna cancel it? in such cases we need job. But thing to be notice here these job children are totally associated with parent. And Parent is (IO) and childrens are (Main), this parent is started in IO Disptacher but when it comes to those childrens they are gonna switch to (Main) and do their thing but the parent will still be at (IO) switching the Dispatcher of childrens not gonna effect parent.
But what happens if something wrong happens to either of the children,
in that case we will watch this summit:
https://www.youtube.com/watch?v=w0kfnydnFWI
This summit about coroutine exception and cancellation. watch it, its amazing...
withContext:
What is withContext?
withContext should be inside any Coroutine or suspend fun because withContext itself is a suspending function.
withContext is use to switch the context in different situation
but how?
suspend fun fetchFromNetworkAndUpdateUI() {
withContext(IO){
println("Some Fake data from network")
}
withContext(Main){
//updating Ui
//setting that Data to some TextView etc
}
}
see the code, we are fetching the data asynchronously from network cause we don't wanna block the MainThread and then we switch the context, why? cause we can't update UI related stuff in IoDispatcher that's we have change the context to main with withContext(main){} and update the UI.
and there are other use cases like liveData, we are fetching the value using retrofit using IoDispatcher then in next step we have to set it to the liveData by using withContext(main){} cause we can't observe liveData's value in background thread.
yeah, I hope this helps. comment if there is any question.
From the Antonio Leiva article about coroutines:
The coroutine context is a set of rules and configurations that define
how the coroutine will be executed
withContext is a function that allows you to easily change the context of a suspending function, in order to be sure that that function is executed in a particular thread (E.g. Thread from IO pool). To do so you can force a suspending function to execute its body within a particular thread pool, for example:
suspend fun getAuthenticationStatus(): AuthenticationStatus = withContext(Dispatchers.IO) {
when (val result = repository.getAuthenticationStatus()) {
is Result.Success -> result.data
is Result.Error -> AuthenticationStatus.Unauthorized
}
}
This way, even if you're calling this suspending function from a UI scope (MainScope), you are 100% sure that the suspending function is executed in a worker thread and you can update the UI with the returned result in the main thread, such as:
MainScope().launch {
userIdentityVM.getAuthenticationStatus().run {
when (this) {
is AuthenticationStatus.Authenticated -> {
// do something
}
is AuthenticationStatus.Unauthorized -> {
// do something else
}
}
}
}
To sum up, by using withContext you can make your suspending function "Main Safe".
The difference between scope and context is basically the intended purpose.
To launch a coroutine you normally use launch coroutine builder, defined as an extension function on CoroutineScope.
fun CoroutineScope.launch(
context: CoroutineContext = EmptyCoroutineContext,
// ...
): Job
The context specified as parameter on the coroutine scope is merged to coroutine scope by plus operator and takes precedence on the "default" context specified by coroutine scope. This way you can execute the code in a "parent" context. To go deep I suggest you this article by Roman Elizarov (Team Lead for Kotlin libraries #JetBrains).

Kotlin coroutines `runBlocking`

I am learning Kotlin coroutines. I've read that runBlocking is the way to bridge synchronous and asynchronous code. But what is the performance gain if the runBlocking stops the UI thread?
For example, I need to query a database in Android:
val result: Int
get() = runBlocking { queryDatabase().await() }
private fun queryDatabase(): Deferred<Int> {
return async {
var cursor: Cursor? = null
var queryResult: Int = 0
val sqlQuery = "SELECT COUNT(ID) FROM TABLE..."
try {
cursor = getHelper().readableDatabase.query(sqlQuery)
cursor?.moveToFirst()
queryResult = cursor?.getInt(0) ?: 0
} catch (e: Exception) {
Log.e(TAG, e.localizedMessage)
} finally {
cursor?.close()
}
return#async queryResult
}
}
Querying the database would stop the main thread, so it seems that it would take the same amount of time as synchronous code? Please correct me if I am missing something.
runBlocking is the way to bridge synchronous and asynchronous code
I keep bumping into this phrase and it's very misleading.
runBlocking is almost never a tool you use in production. It undoes the asynchronous, non-blocking nature of coroutines. You can use it if you happen to already have some coroutine-based code that you want to use in a context where coroutines provide no value: in blocking calls. One typical use is JUnit testing, where the test method must just sit and wait for the coroutine to complete.
You can also use it while playing around with coroutines, inside your main method.
The misuse of runBlocking has become so widespread that the Kotlin team actually tried to add a fail-fast check which would immediately crash your code if you call it on the UI thread. By the time they did this, it was already breaking so much code that they had to remove it.
Actually you use runBlocking to call suspending functions in "blocking" code that otherwise wouldn't be callable there or in other words: you use it to call suspend functions outside of the coroutine context (in your example the block passed to async is the suspend function). Also (more obvious, as the name itself implies already), the call then is a blocking call. So in your example it is executed as if there wasn't something like async in place. It waits (blocks interruptibly) until everything within the runBlocking-block is finished.
For example assume a function in your library as follows:
suspend fun demo() : Any = TODO()
This method would not be callable from, e.g. main. For such a case you use runBlocking then, e.g.:
fun main(args: Array<String>) {
// demo() // this alone wouldn't compile... Error:() Kotlin: Suspend function 'demo' should be called only from a coroutine or another suspend function
// whereas the following works as intended:
runBlocking {
demo()
} // it also waits until demo()-call is finished which wouldn't happen if you use launch
}
Regarding performance gain: actually your application may rather be more responsive instead of being more performant (sometimes also more performant, e.g. if you have multiple parallel actions instead of several sequential ones). In your example however you already block when you assign the variable, so I would say that your app doesn't get more responsive yet. You may rather want to call your query asynchronously and then update the UI as soon as the response is available. So you basically just omit runBlocking and rather use something like launch. You may also be interested in Guide to UI programming with coroutines.

Categories

Resources