Room - Slow Query in Background - android

I'm currently implementing Room to replace my old SQL code, but I'm running into an issue where my query is very slow when running in the background.
For example, I have two identical queries, one that runs on the UI thread, and another that returns a Single. I'm using allowMainThreadQueries() to test this case.
#Query("SELECT * FROM event ORDER BY `begin` ASC LIMIT $LIMIT")
fun getUIThreadSchedule(): List<Event>
#Query("SELECT * FROM event ORDER BY `begin` ASC LIMIT $LIMIT")
fun getSchedule(): Single<List<Event>>
Now, when I run both of these and compare the time to give me a result, they are very different.
This will take ~6ms to complete.
val events = database.getUIThreadSchedule()
And this will take ~360ms to complete.
database.getSchedule()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({
// elements are now here
}, {
// show an error view
})
I tried using other options, such as Flowable, but the result is the same.
Does anyone know what I could be doing wrong?
Thanks.

After a lot of looking into the issue, I found the why this call was taking longer than the blocking call.
When calling getSchedule(), the subscribe block is not run right when the query is completed. It has to wait for the UI thread, so if that's blocked in another aspect, it will have to wait.
// start query
database.getSchedule()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({
// once ui thread is available, display content
}, {
// ...
})
The reason my UI thread was blocked is because I'm testing on cold start, so my Fragment would created, my query would be fired off, but then it would have to wait for the first frame of the rest of the UI to render before I could handle the getSchedule() result.
With the blocking call, it already had the UI thread, so there was no waiting.

Related

Where to put reused values used for every emission in Observable/ Flowable?

I have some expensive operations that only need to be performed once (e.g. load/ download large files, load large ML models, or calculate optimized data structure based on some other data). I want to use this for every value the Observable/ Flowable generates:
The following code works, but it runs heavyProcessing() and heavyProcessing2() on the caller's thread. In my case, I can't choose what my callers thread (its the main thread because I am using WorkManager's RxWorker, which calls createWork from main). Therefore, start blocks the main thread. How do I get heavyProcessing to be performed in the background with RxJava and also available to the subsequent RxJava chain?
fun start(): Observable<Unit> {
val heavy = heavyProcessing() // the heavy value i want to use everywhere!
val anotherHeavyObject = heavyProcessing2()
val items = Observable.fromIterable(listOfHundredsOfItems)
.map { doSomeWork(it, heavy) }
.map { doSomeWork(it, anotherHeavyObject) }
}
My attempts has so far not worked:
Create a wrapper around the existing function: The issue with this code is the Observable returned by start() does not get observed, so the doSomeWork doesn't actually get done. I only know this because I put breakpoints in at doSomeWork, and it never gets called.
fun startInBackground(): Single<Unit> {
return Single.fromCallable {
start()
}
}
I've been trying to find ways of 'unnesting' the inner Observable (inside the Single), as that's probably the issue here. The inner Observable is not being observed.
This RxJava stuff is very unintuitive even after reading the guide
Yes, it was related to Deferred-dependent. The example in the docs state:
Sometimes, there is an implicit data dependency between the previous sequence and the new sequence that, for some reason, was not flowing through the "regular channels". One would be inclined to write such continuations as follows:
AtomicInteger count = new AtomicInteger();
Observable.range(1, 10)
.doOnNext(ignored -> count.incrementAndGet())
.ignoreElements()
.andThen(Single.defer(() -> Single.just(count.get())))
.subscribe(System.out::println);
Actually, all I needed the caller to do is:
Single.defer { start() }.map { doMoreWork() }
instead of
start().map { doMoreWork() }

Convert Observable<List<T>> to List<T>

I want to get MutableLiveData<List<Country>>() object from Observable<List<Country>>, but I'm not able to find a way.
I'm using implementation below, but it's not working. Is there any other way to achieve it, or am I doing something wrong with my current implementation?
dataManagerAnonymous.countries.toList().blockingGet().single()
The above code shows NetworkOnMainThreadException and crashes the Application.
dataManagerAnonymous
.countires
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe { result ->
// result is List<Country>
}
dataManagerAnonymous.countires probably perform network request, which should be done on background thread in order to prevent UI thread from blocking. subscribeOn(Schedulers.io()) invokes your request on background thread. observeOn(AndroidSchedulers.mainThread()) lets you to use result of this call on UI thread. Subscribe block will be called when your request had been completed successfully and result data is ready to process.

How to perform n number of operations parallely in rxjava

I have a scenario. I am getting data from different sources in different time. I need to perform some intensive work on those data parallely on a background thread and get the result on the main thread. I thought of using BehaviorSubject. But I am not able to figure out how will I do the operation parallely and return the result back to main thread. Since subscribeOn doesn't have any effect in Subjects so I wont be able to perform the operation in background thread. Am I missing something to connect?
Can you try something like this pls:
Observable.merge(
Observable.just(1, 2, 3).subscribeOn(Schedulers.io()),
Observable.just("4", "5", "6").subscribeOn(Schedulers.io())
)
.observeOn(AndroidSchedulers.mainThread())
.subscribe();
I recommend to take a look to ParallelFlowable and to try the example below
someFlowable
.parallel(NUMBER_OF_THREADS)
.runOn(scheduler)
.doOnNext(this::doSomeWork)
.sequential()
.subscribe{ result -> ... }

Starting an asynchronous operation using RxJava without subscribing to an observable?

Let's say your DAO has this method that updates user records in the DB:
#Update
fun update(user: User): Single<Int>
Recently I started learning RxJava and so far I have seen lots examples like following:
// Example 1
disposable.add(dao.updateUser(user)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
Log.d(TAG, "response received")
}
In the above example, I understand that as soon as the subscription starts, updateUser() will be executed on a worker thread and the subscriber will be notified and run in the main thread once the execution completes.
But what if you are not interested in the result of updateUser(), and all you want is just to execute updateUser() on a worker thread?
So far I have tried doing:
// Example 2
dao.updateUser(user)
or
// Example 3
dao.updateUser(user).subscribeOn(Schedulers.io())
But they didn't work. It seems the update requests are never executed, nothing was logged and records didn't change. I am guessing that's because there isn't any subscriber attached to it.
For now I am forcing it to work by attaching a random subscriber that doesn't really do anything like the one in Example 1. One of the problems with the approach is that I might need to make this request a lot and that might create a lot of dummy subscribers, not to mention that the code looks really bad.
Could you help me find a better way of handling this?
But You already wrote answer for Your question.
You can just call:
dao.updateUser(user).subscribe()
If You want manipulate / jump between thread you are doing something like in Example 1.

SqlBrite Queries on Worker Threads

I'm still somewhat new to rxAndroid/rxJava so I'm having a little trouble wrapping my mind around this and if I'm doing everything correctly.
I am using SqlBrite to update a RecyclerView with rows returned from a Cursor. When a db operation is performed on the table, the observable below is responsible for re-querying the data. I want to return a CursorWrapper (ChecklistCursor), but want to make sure I am running the select * query on a worker thread. The checklistRecyclerAdapter subscribes to the ChecklistCursor observable and is responsible for managing the cursor.
The code below seems to be the only way that I am able to get query.run() to run on a worker thread and the subscription to return on the main thread.
I guess it works, but I don't believe this would be the standard way to do this. Could anyone offer a better method than using observeOn twice?
compositeSubscription.add(db.createQuery(Checklist.TABLE, Checklist.QUERY_ALL)
.observeOn(Schedulers.newThread())
.map(query -> new ChecklistCursor(query.run()))
.observeOn(AndroidSchedulers.mainThread())
.subscribe(checklistListRecyclerAdapter));
I would replace the first observeOn call with subscribeOn(Schedulers.io()), in order to perform the query operation in a background thread. Map operator will still perform the operation in the background thread, and after that, you can change with observeOn to update your adapter. I think this is the only way to do this.

Categories

Resources