This question is in regard to the best practices in Android programming using MVVM, LiveData, Room (and hence also RetroFit) and co-routines. One of the best practises states that
Long running executions such as Network - or Database calls should be performed asynchronous from the UI thread.
Current documentation and blogs explain how to do this in detail using co-routines and some examples are demonstrating this nicely, e.g. the Sunflower App.
The part I am missing is when a ViewModel is initialized and it needs to show content from the database/repository/network, how the loading using co-routines is done. In the Sunflower App the repository is returning LiveData, but there is no use of co-routines.
Example:
In PlantDao we see:
#Query("SELECT * FROM plants WHERE id = :plantId")
fun getPlant(plantId: String): LiveData<Plant>
There is no suspend keyword hence, this is not part of a co-routine.
In plantRepository there is:
fun getPlant(plantId: String) = plantDao.getPlant(plantId)
Again no suspend keyword so no co-routine.
In the PlantDetailViewModel the initialization shows us
val plant = plantRepository.getPlant(plantId)
So no scope, Job or any co-routine related stuff.
My questions:
Is room performing the DB query asynchronous? And if so, is it using co-routines?
Is this a good practice? Because the repo is only returning LiveData and can only be used to return LiveData
What are other strategies to do this? Any examples?
Would this strategy differ for network requests?
Is room performing the DB query asynchronous? And if so, is it using co-routines?
It is performing asynchronous and no, it does not use coroutines. LiveData is lifecycle aware, so it's called only when it is observed by a resumed LifecycleOwner, like a Fragment.
Is this a good practice? Because the repo is only returning LiveData and can only be used to return LiveData
Kind of. If you watch https://www.youtube.com/watch?v=zbYYoL7vo9Y and https://www.youtube.com/watch?v=B8ppnjGPAGE, you can see that they're steering away from using LiveData in your repo or datasource and instead use coroutines in those layers. The important thing is to understand which coroutine scope your call belongs to. F.i., should it finish when the user does not see the result?
What are other strategies to do this? Any examples? Would this strategy differ for network requests?
The new hot thing in Android town is to use coroutines in combination with Flow. If you use Retrofit for network calls, this now supports coroutines and as well. A nice code lab to check out is this one: https://codelabs.developers.google.com/codelabs/kotlin-coroutines/#0
Related
Question about using LiveData.
With LiveData you get for free that something like:
listener/subscriber support;
lifeCycle awareness/management;
cross thread marshaling, etc.
We could just use the liveData as the mechanism of delivering between any data repository to ui presentation for almost any case.
However in order to using it, it must bring in some objects that it needed, just like if you were to implement those features yourself there must be some supporting classes to be implemented.
Wondering how much/big the overhead it might be? Is it at a lever of could be simply ignored?
The case like do a search it could use LiveData, ui asking result from repository and observes a liveData, the repository posts the result and UI gets notified.
The same could be done without liveData as well (i.e. run coroutines suspended function to fetch from repository directly).
Would like to know whether the LiveData will bring some unnecessary objects, or the benefit over weigh them.
Saw some post but did not find an official guid on when should use/ or not use LiveData, or LiveData is not suitable for such and such cases. Maybe it's just no overhead at all?
Any suggestion/thought?
When working with MVVM (Model View ViewModel) you have two primary options to send data from the ViewModel to the View (updating the View).
Data Binding
LiveData
If you don't want to complicate the XML layout we usually use LiveData, which works based on Observer Design Pattern.
Using Kotlin Coroutines won't bring the full-featured package of LiveData.
With LiveData you have postValue from another thread to main thread,
You can observe changes from inside the view, and ...
PLUS: Kotlin Coroutines are for multithreading which is not relevant to LiveData, a component for holding data.
Recently kotlin flow is gaining a lot of attention. I have never done any reactive programming before so i thought now is a good time to learn it. Even though I have access to books and some articles I could not understand how to integrate it say on an existing app that does not have any rxjava. I tried looking for some sample but the only thing they would give me is very basic. Im really confuse about this reactive programming thing. For example, I have a list that I needed to get on database. Why would I use flow to get that data? If I visualize it as streams, that would give me one data each. While if I get that list I could get the whole list without waiting for each streams to come if I had use flow. I read a lot of articles about this kotlin flow, even rx java. But still, I wanted to understand why streams and how is it any different from other way like the example I just gave?
For example, I have a list that I needed to get on database. Why would I use flow to get that data?
Well, that depends entirely on what you are using to access that database and how it uses Flow.
Let's suppose that you are using Room from the Android Jetpack. In that case, you can use Kotlin coroutines in two ways, via suspend functions and via Flow:
#Query("SELECT * FROM stuff")
suspend fun getStuff(): List<Stuff>
#Query("SELECT * FROM stuff")
fun getStuffNowPlusChanges(): Flow<List<Stuff>>
In both cases, Room will do the database I/O on a background thread, and you can use coroutines to get the results on your desired thread (e.g., Android's main application thread). And initially, the results will be the same: you get a List<Stuff> representing the current contents of the stuff table.
The difference is what happens when the data changes.
In the case of the suspend function, you get just the one List<Stuff> from the point when you call the function. If you change the data in the stuff table, you would need to arrange to call that function again.
However, in the case of the Flow-returning function, if you change the data in the stuff table while you still have an observer of that Flow, the observer will get a fresh List<Stuff> automatically. You do not need to manually call some function again — Room handles that for you.
You will have to decide whether that particular feature is useful to you or not. And if you are using something else for database access, you will need to see if it supports Flow and how Flow is used.
So I know Room does not handle threading so it's up to the dev to ensure it doesnt run queries on the main thread.
Wrapping all queries in AsyncTasks seem incredibly cumbersome but I realize I can use LiveData instead. However, I'm assuming that's only viable for data queries and not Insert and Delete queries? So am I still expected to wrap those in an AsyncTask (without resorting to other third party libraries?) or is there a better option?
So I know Room does not handle threading so it's up to the dev to ensure it doesnt run queries on the main thread.
Room handles threading if you use suspend in Kotlin or a reactive return type for your DAO functions:
Kotlin Flow (requires Room 2.2.0 or higher)
LiveData
An RxJava type (e.g., Observable, Single, Completable)
If you choose to use none of those things, then yes, threading is up to you.
Wrapping all queries in AsyncTasks seem incredibly cumbersome but I realize I can use LiveData instead. However, I'm assuming that's only viable for data queries and not Insert and Delete queries?
If you mean #Query methods that do an INSERT or DELETE instead of a SELECT, then yes, I think you are correct. Since #Insert functions can return a Long, though, you might be able to have a #Query that uses INSERT return a LiveData<Long>. I have not tried this and I suspect that the Room compiler will not recognize that approach, but there is always hope. :-)
So am I still expected to wrap those in an AsyncTask (without resorting to other third party libraries?) or is there a better option?
You are welcome to use an ordinary Thread, or an Executor, a JobIntentService, or anything else in Android that gives you a background thread. AsyncTask in particular is obsolete. If you are going to use modern things like Room, use modern things across the board (e.g., Kotlin with coroutines).
Personally, I would recommend suspend (for Kotlin developers) or Completable (for Java developers using RxJava).
Use RxJava
Create an Observable and write your logic inside it. You can subscribe the observable and get the boolean.
public Observable<Boolean> insertUser(User m) {
return Observable.create(new ObservableOnSubscribe<Boolean>() {
#Override
public void subscribe(ObservableEmitter<Boolean> e) {
appDb.userDAO().insertUsers(m);
e.onNext(true);
e.onComplete();
}
}).subscribeOn(Schedulers.io());
}
Use Coroutine
I am starting to work with new things that are developed by the developer community Android, one of them is Coroutines. I have used the LiveData
and I assumed while using them, they are also lifecycle safe, then why coroutines are introduced and how they are different from LiveData. I have seen the video on Coroutines at Youtube, from developer channel, but I don't understand that completely. How Suspend and Resume works better than LiveData.
Ok first of all coroutines don't really relate too much with LiveData although they might share here and there some concepts.
Coroutines are used to perform async operation: Retreive data from network, database etc.
Coroutines can be used as "LiveData" if you are talking in the context of Channels or Flows (which I don't recomend because you will lose the lifecycle in it). With coroutines you can switch to threads easily.
Suspend functions are just functions that hold and don't run directly. Any suspending function should be inside a coroutine.
The simplest use case I can give you is this:
runBlocking{
//you are inside of a coroutine
val data = getDataFromBackground()
}
suspend fun getDataFromBackground(): SomeDataType = receiveSomeData()
The receiveSomeData method is also marked with suspend keyword.
But of course there is a lot more. The documentation is perfect way to start.
I also have a personal article about coroutines, you may find them easy there.
There is only one point I can think of that you can replace data with coroutines, and that's using Channels. The view won't be observing for LiveData but will be consuming values comming from a channel, created and shared with DI or something.
EDIT:
If you really want to use LiveData + coroutines please check this awesome library by the Android team.
Coroutines is for asynchronous job. Live Data are used to update your View (Activity & Fragment)
I have an Android app based on MVP + interactors + repositories. All layers from repository to presenter are wired using reactive streams (RxJava 2). View requests something from presenter, presenter asks from interactor, interactor asks from repository and repository asks from API itself. Response passes through the same layers as reactive stream from API to presenter. Each layer can map data for underlying layer.
The question is. Where should I call subscribeOn(io()/computation()/etc) and observeOn(AndroidSchedulers.mainThread()) ?
I think that observeOn(AndroidSchedulers.mainThread()) should be called from presenter because heavy computings could be performed in interactor.
In many examples subscribeOn(io()/computation()/etc) is called from presenter, but I don't agree with this approach. I think that presenter shouldn't decide in which thread to load data. Repository should decide in which thread to load data from API.
For example if we have the repository interface to load contacts. An implementation can get data from DB or Internet or in-memory storage. There is no need to create thread for in-memory repository. So repository should decide whether subscribe on io/computation/etc scheduler or not.
Any ideas?
I would not call subscribeOn() and observeOn() in the repository layer for the following reasons:
Methods that implicitly execute on different threads are very hard to use and may cause problems in the clients that are calling them.
If you want to compose multiple streams coming from different repositories, it would be more convenient to have control over them. If they are subscribed on different threads, this may lead to subtle bugs, less than optimal performance and hard to read code.
This adds another responsibility to the repo layer. Instead of being simple data stores, the repos are now aware of threads. In my opinion, this breaks the single responsibility principle.
I generally call subscribeOn() and observeOn() in the last layer before the views - in that case this will be your presenter layer. There I have full control over the composition of the interactors and better judgement over where I want to process things and where do I need the results of the processing.