Converting a Future<void> into RxJava Observable or Flowable - android

How do I convert a future returning void into an RxJava Flowable or Observable?
I am using RxJava and AndroidX WorkManager which provides an API which returns a Future<void>. I understand RxJava does not handle null values and will NullPointerException immediately. I am using Flowable.fromFuture(resultFuture), where resultFuture is Future<Void> (specifically ListenableFuture<Void>), but since it returns Null, it crashes the app.
Motivation: I want to turn this future into a Rx observable/ flowable so that I can do processing after this future completes.
return Flowable.fromFuture(futureReturningVoid)
.flatMap { Flowable.fromIterable(files) }
...more processing here...
I need to return a Single at the end, so I cannot move the work into a listener, with Future.addListener.
I need flowable because I am dealing with processing of multiple input files, and want the backpressure to prevent opening too many files at once. I included Observable in case people have less complex requirements.

So why not :
return Completable.fromFuture(futureReturningVoid)
.andThen(Flowable.fromIterable(files))
Which is basically :
return Completable.fromAction { futureReturningVoid.get() }
.andThen(Flowable.fromIterable(files))

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

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!

RxJava resilent way to combine multiple observables

I have multiple modules that return Observables: O1, O2, O3... On
The result of all modules should be combined into one observable Ocomb so that individual tasks can fail but the combination is not terminated or influenced by individual issues.
With my current solution I'm encountering various problems as in the following example:
This code combines the output of my modules:
public Observable<Data> getModuleData(){
List<Observable<Data>> tasks = new ArrayList<>();
for(MyModule module : modules){
tasks.add(module.getData());
}
return Observable
.mergeDelayError(Observable.from(tasks))
.onBackpressureBuffer(MAX_BUFFER)
.observeOn(AndroidSchedulers.mainThread());
}
Now, I want to display attribute X e.g. 'name' of all emitted data objects:
public List<String> getNames() {
return getModuleData()
.map(new Func1<Data, String>() {
#Override
public String call(Data data) {
return data.getName();
}
})
.timeout(600, TimeUnit.MILLISECONDS)
.toList()
.toBlocking()
.firstOrDefault(new ArrayList<String>());
}
The getNames() method should return a list and therefore block the execution.
Problem 1
It seems there is an issue in RxJava that if I call observeOn() and make it blocking it will not return no matter what timeout etc are saying.
Problem 2
If onObserve() is removed, the code will work but in a different place of the app I'm rendering the results of the non-blocking observable in the UI. Data will be displayed but afterwards my UI does crazy stuff. I have to touch my UI list component to refresh the screen every time data changes.
Problem 3
Some of the modules might create internal errors or will not call onCompleted(). I thought that a combination of mergeDelayError() and timeout() could handle these cases and call onCompleted() for unresponsive modules. However, if one of the modules does not call onCompleted() and the timeout() statement is removed the blocking call will never return.
Questions:
What is the best way to combine multiple observable so that individual observables can fail but it's handled as onCompleted() / ignored and does not affect the combined observable?
What is the best solution to make the combined observable blocking and handle the timeout without stopping the execution or ending up in a loop?
What is the best way to combine multiple observable so that individual observables can fail but it's handled as onCompleted() / ignored and does not affect the combined observable?
Observable.from(modules)
.flatMap(MyModule::getData)
.onErrorResumeNext(Observable.empty())
.timeout(600,TimeUnit.MILLISECONDS, Observable.empty())
.toList()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(System.out::println);
Above stream could be converted to blocking by adding toBlocking before subscribe(), but does not make much sense beyond testing

RXJava Android - Observable result needed to create another observable

I cant find a way to combine or chain a list of observables that it´s responses are prerequisites to other call that creates another Observable.
I´m using retrofit with observables.
My Service:
String url = "/geocode/json?sensor=false";
#GET(url)
Observable<GeocodeResult> getReverse(#Query("key") String gMapsKey,
#Query("latlng") LatLng origin);
And another service needs that GeocodeResult
#POST("/api/orders")
Observable<Order> createOrder(#Body GeocodeResult newOrder);
And I´m trying with:
// Prerequisite 1
Observable geocodeObservable = Address.get(...);
// Call createOrder after geocode is obtained?
return Observable.combineLatest(geocodeObservable, geocode -> createOrder(geocode));
But it don´t work because combineLatest needs an object, not an observable but I need to return the observable.
With JoinObservable:
Pattern5<Geocode> pattern = JoinObservable.from(geocodeObservable)
Plan0<Observable<Order>> plan = pattern.then(Order::create);
return JoinObservable.when(plan).toObservable().toBlocking().single();
But it throws an NoSuchElementException exception. Why?
I do toBlocking().single() because I need the Observable and not the Observable<Observable<Order>> :(.
Or how can I do it?
You could try using flatMap which can take the second observable as an parameter.
The function takes the items emitted by the first observable and creates an observable for each of those items and then flattens the items emitted by those observables into a single observable. This sounds complex, but fortunately both your Retrofit functions emit only a single item, so only one observable gets "flattened" into a observable.
You can use flatMap like this:
restApi.getReverse(gMapsKey, origin)
.flatMap(geocodeResult -> createOrder(geocodeResult))
.subscribe(order -> doSomething(order));
combineLatest doesn't really fit your needs, because it would perform both REST calls at the same time, not one after the other, so you can't use the response of the first one as the parameter of the second. I can't comment on why the exception gets thrown for JoinObservable because it's not a part of any public API. Also toBlocking() shouldn't really be used for anything other than testing.
I ended up creating a new Object and using Observable.combineLatest to combine all the prerequisites creating a new Observable and then using flatMap to create the new Observable from that observable.
Observable<NewOrderWrapper> newOrderObservable = Observable.combineLatest(prerequisites, (param1, param2,...) -> {return new NewOrderWrapper(param1, param2,...)});
and then
Observable<Order> finalOrderObservable = newOrderObservable.flatMap(newOrderWrapper -> create(newOrderWrapper))
Check a post here MakinGIANST/RXJava post.
Thanks to #LukaCiko

Composing observables in RxJava Android

I've been adapting my Android app to use RxJava but I'm having a little bit of trouble doing so. As I had been advised in a previous post (Wait for all requests in Android Volley), I'm using Observables to mimic how I'm interfacing with my REST API in JavaScript. Specifically, using the promise library, I compose calls like this:
$q.all([
fetchResourceA(),
fetchResourceB()
])
.then(function (responses) {
...
return fetchResourceC();
})
.then(function (response) {
...
});
In this example, I query two resources simultaneously, collect the results, then collect a third resource based on some of the parameters from the previously collected resources. The best I've been able to do to mimic this in RxJava is like this:
Observable o = Observable.zip(
fetchResourceA(),
fetchResourceB(),
new Func2<ResA, ResB, Object>() {
#Override
public Object call(ResA resA, ResB resB) {
...
}
}
);
But I'm struggling to compose them like I did in JavaScript. Do I need to simply create a second observable and subscribe to it in the callback of the zip? That's what I'm doing now, and it works, but I'd like to know if there's a more elegant and more reactive-appropriate way to structure my requests.
The .then method from promise can be transposed to flatMap method in RxJava
So, what you can do, is to zip then flatMap then flatMap
Observable.zip(fetchA(), fetchB(), (a, b) -> new Response(a, b))
.flatMap((responses) -> fetchC())
.flatMap((cResponse) -> /* whatever */)
.subscribe();
Please note that fetchA(), fetchB(), fetchC() return Observables.
(My example use lambdas for clarity)

Categories

Resources