Room, threading and insert/delete queries - android

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

Related

Understanding reactive

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.

Displaying LiveData in ViewModel from Room with co-routines

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

What are the Kotlin Coroutines, How they are different from live data

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)

How to properly approach threading with Room

I am confused about Room and can not find an answer in the documentation.
So, the library enforces using queries on a separate Thread, which is understandable. However, it seems that Delete queries are not included in this limitation and can be freely called from the UI Thread. They also always return a raw output value, without a chance to wrap it in an Observable.
What is the correct way to use the delete call in Room then? Should it be run on a separate Thread? If not, what about performance and concurrent modifications?
If you use LiveData to retrieve data from Room, it's executed in worker thread. For, other queries you can use Executors and Repository pattern. You can check out this page for guide to app architecture.
You can check out this link for Rx and other architecture component samples.
Analysis note by the question author:
In the sample they use a Completable to wrap the Room delete call and then schedule it onto the io() scheduler, reacting to the empty complete and any errors. That specific code can be found here.

Tactic for several different Room queries

While refractoring an app I decided to use room (and other architecture components). Everything went well until I reached database querying, which is async. It is fine by me, I can update views with LiveData callbacks.
But the problem arose with smaller queries following each other - with no thread restrictions it was easy, you could use variables straight away.
In legacy code there are plenty of setups, where quite many small data pieces are required one after another, from different tables. E.g., querying if item exists in one table, some calculations, querying another table, etc.
Disabling async requirement for queries is not an option, I prefer using Room as intended.
First thought was to nest callbacks, but it is too ugly.
Second thought was to query for all required data, start a method only after receiving all callbacks. It also does not sound nice and there are cases where one callback has data required for the other query.
Strangely I have not found any related forum posts or articles dealing with this problem.
Did anyone handle it already? Any ideas?
Most #Dao methods are synchronous, returning their results on whatever thread you call them on. The exceptions are #Query methods with reactive return types, such as Maybe<List<Goal>> or LiveData<List<Goal>>, where the methods return the reactive type and the results are delivered asynchronously to subscribers.
So, for cases where you have more complex business logic, you have three main courses of action (that I can think of right now):
Use RxJava and try to squish all that business logic into an observable chain. There are a lot of RxJava operators, and so some combination of map(), flatMap(), switchMap(), weAreLostWhereDidWePutTheMap(), etc. might suffice.
Do the work on a background thread, mediated by a LiveData subclass, so the consumer can subscribe to the LiveData.
Use classic threading options (e.g., IntentService) or more modern replacements (e.g., JobIntentService).

Categories

Resources