RxJava2 wrapper for Firebase Firestore without switching threads? - android

I'm trying to create a reactive observable for Firebase Firestore calls.
I'm facing a threading issue. I'm using rxjava2 to handle threads and I don't want Firestore API to do that for me. It seems like Firestore calls are async, thus OnSuccess method is getting called on the main thread
Here is a simple example that showcases the issue:
Single<Integer> firestoreSingle = Single.create(emitter -> {
Log.d("TAG", Thread.currentThread().getName()); // -> RxCachedThreadScheduler-3 Thread
CollectionReference collectionRef = FirebaseFirestore.getInstance().collection("test_collection");
collectionRef.get().addOnSuccessListener(queryDocumentSnapshots -> {
Log.d("TAG",Thread.currentThread().getName()); // -> MAIN THREAD
List<DocumentSnapshot> documentSnapshotList = queryDocumentSnapshots.getDocuments();
emitter.onSuccess(documentSnapshotList.size());
}).addOnFailureListener(emitter::onError);
});
firestoreSingle
.subscribeOn(Schedulers.io())
.subscribe(howManyDocs -> {
Log.d("TAG",Thread.currentThread().getName()); // -> MAIN THREAD
Log.d("TAG","How many docs: " + howManyDocs);
});
Of course, I could add .observeOn(Schedulers.io()) to the reactive stream, but then I would not necessarily get the results on the same thread as the one I initially subscribed on.
I don't want the results neither in the main thread, nor in a different thread that the one I subscribed on.
Is there a way to query Firestore synchronously? How would you solve this issue?

You can use the answer that #MarkKeen suggested in the comment but for reference if you want to stick with RxJava, you can always call the method .blockingGet() to, as it suggests, block until a value is emitted.

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() }

Android Espresso not synchronized with Rx Completable

I have an issue with Espresso not waiting for Completable to finish, so my UI test is Failing
apiDataSource.getData()
.flatMap { data ->
cacheDataSource.saveData(data)
.andThen(Observable.just(cacheDataSource.getData()))
is there a way to hold the Espresso thread until having the cacheDataSource.saveData(data) complete?
Thank you in advance
The reason is that you call cacheDataSource.getData() right when you assemble the save sequence inside flatMap. From the documentation of just:
Note that the item is taken and re-emitted as is and not computed by any means by just. Use fromCallable(Callable) to generate a single item on demand (when Observers subscribe to it).
apiDataSource.getData()
.flatMap { data ->
cacheDataSource.saveData(data)
.andThen(Observable.fromCallable { cacheDataSource.getData() })
}

Editing data in repository pattern using RxJava

I'm refactoring the implementation of my repositories using RxJava so i want to know some ways to edit, for example, a user.
My getUser(email: String), with email as id, is returning an observable and in the repository implementation i either get the data from database or server, all good by now.
What i want to achieve is editing a user. For that i would have and update(user: User) function, and the naive way to use it would be
userRepository.getUser(email)
.subscribeOn(Schedulers.io())
.subscribe { user ->
user.name = "antoher name"
userRepository.update(user)
.subscribeOn(Schedulers.io())
.subscribe {
//handle response
}
}
Is there a way to avoid this type of call of an observer inside an observer? It is not very readable for me and i guess there's a better way but i'm not getting it.
NOTE: I'm using clean architecture, so i think an update for every field, making me get user in data module is not correct as i would have subscribe to an observer in data, and that difficult the dispose when activity destroys
For me is not the same question as When do you use map vs flatMap in RxJava? because, despite of flatMap being the thing that answer the question, it is not the same question, so anyone who has the same problem/question but don't know that flatmap is the answer, will never reach to use flatmap.
One strength of using RxJava is that you can chain as many async operations (method that would return Observable or Single, repository methods in your case) as you want without falling into callback hells. You see in your code that there are nested subscribe blocks. What if you had to chain more async network operations? You fall into callback hells and the code will become harder to follow and maintain.
Removing nested callbacks and making code more functional, compositional, and readable is one thing RxJava is really good at. In the intro part of ReactiveX website , they mention about this in the intro part of ReactiveX website (http://reactivex.io/intro.html).
Callbacks solve the problem of premature blocking on Future.get() by
not allowing anything to block. They are naturally efficient because
they execute when the response is ready.
But as with Futures, while callbacks are easy to use with a single
level of asynchronous execution, with nested composition they become
unwieldy.
Flatmap operator is to the rescue here. You can look into the definition of flatMap operator in the link below.
http://reactivex.io/documentation/operators/flatmap.html
Below is the code I would use in your case.
userRepository.getUser(email)
.subscribeOn(Schedulers.io())
.map { user -> user.name = "another name"; return user; }
.flatMap { user -> userRepository.update(user) }
.doOnSuccess { /* handle response here */ } // doOnNext if you are using observable
.subscribe({ /* or handle response here */ }, { /* must handle error here */})
Flatmap operator flattens Single of update response which will be returned by your repository's update method and pass just the response downstream. Above code is not only easier to read but also makes your code reusable because update logic is now part of the chain.
Distinguishing between map and flatMap is really important in exploiting the full benefit of RxJava so it will be really beneficial to get used to it!

How to wrap listeners that always calls its callbacks from a specific thread into an Observable that conforms to the Scheduler defined by subscribeOn?

Brief introduction for those not familiar with Android and/or Firebase development:
In Android development, you should always manipulate your application's views from the main thread (also called UI thread), but if your application needs to make some heavy processing, it should use a background thread, otherwise the app would seem unresponsive.
Firebase is a service that offers a way to store and sync data with a NoSQL database in the cloud. It also offers an Android SDK to manage this database. Every time this SDK is used to make an operation, like a query, Firebase avoids those threading pitfalls by making all of its heavy processing on its own internal background thread and by always calling its callbacks on the main thread.
Example:
Query postsQuery = FirebaseDatabase.getInstance().getReference("posts");
ValueEventListener postListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
// This is always called on the main thread
// Get Post object and use the values to update the UI
Post post = dataSnapshot.getValue(Post.class);
// ...
}
#Override
public void onCancelled(DatabaseError databaseError) {
// Getting Post failed, log a message
printError(databaseError.toException());
// ...
}
};
postsQuery.addValueEventListener(postListener);
The actual issue I'm facing:
I'm trying to wrap Firebase's query listeners with RxJava using a method like this:
private static Observable<DataSnapshot> queryObservable(final Query query) {
return Observable.fromEmitter(emitter -> {
// This is called on the Scheduler's thread defined with .subscribeOn()
printThread("emitter");
final ValueEventListener listener = new ValueEventListener() {
#Override public void onDataChange(final DataSnapshot dataSnapshot) {
// This is always called on the main thread
printThread("onDataChange");
emitter.onNext(dataSnapshot);
}
#Override public void onCancelled(final DatabaseError databaseError) {
// This is called on the main thread too
emitter.onError(databaseError.toException());
}
};
query.addValueEventListener(listener);
emitter.setCancellation(() -> query.removeEventListener(listener));
}, Emitter.BackpressureMode.BUFFER);
}
But because the Observable is emitting items from inside the Firebase's callback (called on the main thread) any further .subscribeOn() operators are going to be ignored.
For example, calling the above method like this:
Query postsQuery = FirebaseDatabase.getInstance().getReference("posts");
queryObservable(postsQuery).doOnSubscribe(() -> printThread("onSubscribe"))
.subscribeOn(Schedulers.io())
.subscribe(dataSnapshot -> printThread("onNext"));
Would print the following:
onSubscribe Thread: RxIoScheduler-2
emitter Thread: RxIoScheduler-2
onDataChange Thread: main
onNext Thread: main
From what I understand, when Firebase's SDK calls the onDataChange() callback and switches from its own internal background thread to the main thread, it also makes the Observable emit new items on the main thread, rendering useless any .subscribeOn() operator down the stream.
The actual question:
What can I do to not only correctly wrap listeners like this into an Observable but also make them conform to the Scheduler defined by .subscribeOn()?
Thank you!
Update:
I know .observeOn() gives me the ability to process the data returned by Firebase on another thread. That's what I'm doing already, but it just isn't the point of this question. The point is: when I pass a Scheduler through .subscribeOn() I expect the upstream to conform to that Scheduler's thread but that doesn't happen when the Observable has an internal listener that is being triggered from a callback on a different thread. When that happens, I lose the .subscribeOn() guarantee.
The severity of this issue may not seem obvious at first, but what if that Observable was part of a library? What's the best practice there? Should the library enforce its clients to always call an .observeOn() after any call to that method? Should the library call an .observeOn() itself and call it a "default Scheduler"? In any of these cases the .subscribeOn() is just useless, and that doesn't seem right to me.
Just use observeOn in IO and subscribeOn in Main Thread, in that way you can manage your recieved that in MainThread and move the firebase work to a different Thread.
Remember to import rxAndroid to your gradle(Rxjava or RxJava 2):
compile 'io.reactivex.rxjava2:rxandroid:2.0.1'
Also suggest you to check as reference(or just use it) one of the next libraries:
RxJava : https://github.com/nmoskalenko/RxFirebase
RxJava 2.0: https://github.com/FrangSierra/Rx2Firebase
One of them works with RxJava and the other one with the new RC of RxJava 2.0. If you are interested of it, you can see the differences between both here.
I Had the same problem, finally I combined with Coroutines to run the listener on background.
To do that, simple add coroutine background work in your onDataChange
Kind regards

Example with SubscribeOn / ObserveOn in Xamarin Android

I'm trying to use RX in Andorid Xamarin.
Do you know how to use:
Observable.SubscribeOn(..) to declare using thread from default pool to do background tasks
Observable.ObserveOn(..) to redirect events from Observable to UI thread
According to SubscribeOn I tried all ISchedulers
from
System.Reactive.PlatformServices.dll / System.Reactive.Concurrency
that is:
NewThreadScheduler.Default
TaskPoolScheduler.Default
ThreadPoolScheduler.Instance
and nothing works.
On the ohter hand [if possible] I don't want to manually:
- create my thread inside Observable
- use RunOnUiThread in Observer
= = = update = = =
Test results for NewThreadScheduler.Default
Code:
Console.WriteLine("creating ole");
var ole = Observable.Create<string>(suber =>
{
Console.WriteLine("inside ole");
Thread.Sleep(5000);
suber.OnNext("point1");
suber.OnCompleted();
Console.WriteLine("ole completed");
return Disposable.Create(() => Console.WriteLine("observer unsubscribed"));
});
ole.SubscribeOn(NewThreadScheduler.Default);
Console.WriteLine("subscribing");
oleSub = ole.Subscribe(s => Console.WriteLine("result: " + s));
Console.WriteLine("subscribed");
Output:
creating ole
subscribing
inside ole
result: point1
ole completed
observer unsubscribed
subscribed
Conclusion:
Observable content is executed in main thread, although is expected to run its own thread: ole.SubscribeOn(NewThreadScheduler.Default);
Rx is based on Functional programming, of which a key tenant is side-effect free programming.
when you create var ole = Observable.Create... you are creating an observable sequence. This sequence will have behavior when something subscribes to it.
When you then try to set up the Subscribe on ole.SubscribeOn(NewThreadScheduler.Default); you are decorating the ole observable sequence with SubscribeOn behavior, however as this is a side-effect free operation, it returns a new observable sequence. You don't assign the returned instance to anything. i.e. the ole.SubscribeOn(NewThreadScheduler.Default); line of code does nothing.
You then go back to the original unmodified ole observable sequence and subscribe to that.
I have two suggestions
Create a Logging helper operator to remove all the Console.Write noise in your code. This will both be a useful thing for you to use when debugging your Rx code and will also allow you to learn how to create an Rx operator (that will be side effect free). https://github.com/LeeCampbell/RxCookbook/blob/34ac4f3536b00bbe259384d3bf0e8746da3311cc/Instrumentation/Logging.md
Chain your methods so that you actually get the behavior you want.
Here we use the SubscribeOn method properly. We also use the Log extension method you can write yourself from the link above.
var ole = Observable.Create<string>(obs=>
{
Thread.Sleep(5000); //Dont use Thread.Sleep and Rx :-)
obs.OnNext("point1");
obs.OnCompleted();
return Disposable.Empty;
});
var oleSubscription = ole
.Log("ole")
.SubscribeOn(NewThreadScheduler.Default)
.ObserveOn(/*What ever Android's UI Thread Scheduler is*/)
.Subscribe(s => Console.WriteLine("result: " + s));
Also note that I have added the ObserveOn operator too. I strongly urge users of these two methods to only use them on the final subscriber (probably your ViewModel?) and only use them as the last operators before the Subscribe method as above.
More help :
http://www.introtorx.com/content/v1.0.10621.0/15_SchedulingAndThreading.html
http://www.introtorx.com/content/v1.0.10621.0/18_UsageGuidelines.html#UsageGuidelines

Categories

Resources