How to properly approach threading with Room - android

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.

Related

Kotlin Coroutines. what is in charge of suspend function? how many thread could get involved when working with coroutines?

I have a couple questions on kotlin coroutines.
how many thread could get involved when working with coroutines?
if we use just Dispatchers.Main, would only one thread get involved(single threaded)? if we use Dispatchers.IO, would multiple thread possibly get involved (maximum of 64 threads)?
what will be the use case for using Dispatchers.Main? most articles that I have read say all UI related works should present in Dispatchers.Main and background related works(like reading/writing data from/to database, network requests) needs to present in Dispatchers.IO but I don't understand what UI related works present in Dispatchers.Main since UI related work don't really necessary need coroutines (with Dispatchers.Main)
we use susepnd fuction with coroutines for some works that could block the current thread. For example, read data from disk, network requests, or high tense computation etc. if these works are executed by suspend function, what/who is in charge when these functions are suspended? I think something has to be working on these suspend functions anyway. will that be background threads that is in charge of below?
reading/writing data from/to databse
waiting for network request
computing high tense computation
please point out if my wording or questions are incorrect.
Thank you in advance.
I think you answered yourself. Short answer is: Dispatchers.Main - single thread, Dispatchers.Default - number of cores, Dispatchers.IO - at most 64. You can read about it here. Full answer is a little more complicated as limits could be reconfigured, they may differ on different platforms (e.g. JavaScript is always single-threaded), Default partially shares threads with IO, etc. We can also create our own thread pools.
I'm not sure what do you mean. Coroutines are generally never necessary in order to do anything. But if we use coroutines inside UI application, then we should use Dispatchers.Main for UI-related stuff.
We should almost never use blocking code inside coroutines (one exception is Dispatchers.IO). If we do that, the coroutine won't suspend, but just block, possibly making other parts of our application unresponsive or degrading the performance.

Is Task<T>.await() main-safe?

I'm writing my first Kotlin app and am using firebase services for auth, db & storage. As it is not possible to make an atomic Firestore + Storage operation, I find myself in quit a callback-hell for a simple image upload (with error fallbacks and all). Thus - I decided to refactor my app to use coroutines. I found some examples (like here and here) but I noticed that the repository-level functions in those examples are not wrapped with withContext(Dispatchers.IO){ } like shown in android docs. Should they? I guess this is two questions in one:
Should Firebase operations always be called with the IO dispatcher?
Is kotlinx-coroutines-play-services's Task<T>.await() main-safe?
And a bonus question: I wrap all my Firebase calls in a proxy object for decoupling - is there a way to set all functions of an object (/class) to run with the same context, or do I have to wrap each function with withContext(Dispatchers.IO){ } separately?
Thanks a lot!
Should Firebase operations always be called with the IO dispatcher?
All Firebase APIs are asynchronous and designed to be called safely from the main thread unless otherwise stated in the API documentation.
Is kotlinx-coroutines-play-services's Task.await() main-safe?
Yes. As the API documentation states (emphasis mine):
Awaits for completion of the task without blocking a thread.
It's a suspend fun, and they do not block. However, they do not really make sense to call outside of a coroutine.

Room, threading and insert/delete queries

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

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).

Android - SQLite ContentResolver insert/delete/update on UI Thread?

I have looked through many examples/tutorials of using SQLite in Android. Let's say you have an app that uses SQLite, ContentProvider, CursorLoader, a custom CursorAdapter.
Now all major examples of this that I've found rely on a CursorLoader to fetch data to the CursorAdapter, which by the nature of CursorLoader happens in an Async - UI thread safe manner. However, these same examples all make insert/delete/update calls through the ContentResolver on the main thread (e.g. from onClick, onResume, onPause). (Example) They don't wrap these calls in an AsyncTask or launch a separate thread or use the AsyncQueryHandler.
Why is this, how can so many well written blogs/examples make such an obvious mistake? Or are simple single row insert/delete/update calls so quick that they are safe enough to launch from the Main/UI thread? What is the proper way to do these quick calls?
I also got confused about the samples making calls on the main thread. I guess the samples just simplified the demonstrations avoiding extra threads and callbacks, since single insert/update/delete call may return quickly.
Besides the Loader pattern for query, android did provide a helper class AsyncQueryHandler, since API level 1, for async CRUD operations with full CRUD callbacks supported. The AsyncQueryHandler works inside with a HandlerThread for the async operations and delivers the results back to the main thread.
So I do believe the ContentProvider queries should run in worker threads other than the UI, and those samples may not be best practices according to the official design.
=== edit
Found an annotation from the official framework docs, see this or this, Line 255:
In practice, this should be done in an asynchronous thread instead of
on the main thread. For more discussion, see Loaders. If you are not
just reading data but modifying it, see {#link android.content.AsyncQueryHandler}.
=== edit 2
Link to actual android dev guide containing the above quote
This question has been on my mind since a long time. I guess, this depends on the complexity of the file we are trying to Insert, Update or Delete. If our application is going to Insert or Update large files, it would be always right to do it asynchronously and if the files aren't going to be that big, running it on UI thread can be done.
However, it is always recommended to continue with Database operations on a separate thread.
I think you've answered your own question. I do believe CursorLoader extends AsyncTaskLoader. Calls made from UI thread only process the call TO the CusorLoader (which uses AsyncTask.) What is being done BY the call still does not occur on UI Thread. Making a call to a method/function that then runs things on a seperate thread is still doing work away from UI thread.
What work do you think is happening on the UI thread?
Please show Debug log if possible or example where you think work is done on UI.
It shouldn't be.
Not trying to argue just want to know how you've come to the conclusion of UI work?

Categories

Resources