Chaining N dependant observables - android

I'm quite new to reactive programming and I've introduced myself to RxJava2 in Android. For the time being, I've faced easy problems, such as zipping observables. But now, something new cropped up, I'm trying to explain.
Suppose I've got a list of requests Observable<List<Request>>. What I want to do is to call a web service which returns per each request, the list of routes (wrapped in an Observable). I've checked questions like this, but in this case I think I can't flatMap an observable and a list of observables.
How can I do it? Is there any other operator?

You can flatten the Observable<List<Request>> into Observable<Request> using flatMapIterable. Assuming you have a helper method with the signature Observable<List<Route>> getListOfRoutes(Request request) { ... } you can do this:
Observable<List<Request>> obs = ...;
obs.flatMapIterable(l -> l)
.flatMap(request -> getListOfRoutes(request)
.doOnNext(routes -> request.setRoutes(routes))
.map(ign -> request)
)
...
This is assuming that you ultimately want Observable<Request> to be emitted downstream. If you want a different type, you can do something different in the map operator to suit your needs.

Related

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 2 Nested Network Requests

in the app I am currently working on I use retrofit to create an Observable <ArrayList<Party>>.
Party has a hostId field as well as a field of type User which is null at the point of creation by Retrofits GsonConverter. I now want to use hostId to make a second request getting the user from id and adding the User to the initial Party. I have been looking into flatmap but I haven't found an example in which the first observable's results are not only kept but also modified.
Currently, to get all parties without the User I am doing :
Observable<ArrayList<Party>> partiesObs = model.getParties();
partiesObs.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(this::handlePartyResponse, this::handleError);
How would I go about adding User to every Party without having to call model.getUsers() in the onSuccess() method of the inital call and then having to iterate through the two lists?
I understand that flatmap() returns a new Observable while map doesn't but I am unsure about how to use either in this scenario.
Thank you
As in the comment, you should try and get the backend API changed for something like this to avoid an inelegant and inefficient solution.
If this is not feasible, you could probably do something like this:
.flatMapIterable(list -> list)
.flatMap(party -> model.getUser(party.hostId),
(party, user) -> new Party(user, party.hostId, party.dontCare))
Where:
flatMapIterable flattens the Observable<ArrayList<Party>> into an Observable<Party>
The overload of flatMap takes a Function for transforming emissions (Party objects) into an ObservableSource (of User objects) as the first parameter. The second parameter is a BiFunction for combining the Party and User objects which you can use to create a fully fledged Party object.
The last step is much easier if you have a copy or clone operation on the Party object that takes a previous instance and adds fields to it.

RxJava: How to wait for all subscriptions to complete?

I am fairly new to rxJava, trying stuff by my own. I would like to get some advice if I'm doing it right.
Usecase: On the first run of my app, after a successful login I have to download and save in a local database several dictionaries for the app to run with. The user has to wait till the downloading process finishes.
Current solution: I am using retrofit 2 with rxjava adapter in order to get the data. I am bundling all Observables into one using the zip operator. After all downloads are done the callback triggers and saving into database begins.
Nothing speaks better than some code:
Observable<List<OrderType>> orderTypesObservable = backendService.getOrderTypes();
Observable<List<OrderStatus>> orderStatusObservable = mockBackendService.getOrderStatuses();
Observable<List<Priority>> prioritiesObservable = backendService.getPriorities();
return Observable.zip(orderTypesObservable,
orderStatusObservable,
prioritiesObservable,
(orderTypes, orderStatuses, priorities) -> {
orderTypeDao.deleteAll();
orderTypeDao.insertInTx(orderTypes);
orderStatusDao.deleteAll();
orderStatusDao.insertInTx(orderStatuses);
priorityDao.deleteAll();
priorityDao.insertInTx(priorities);
return null;
});
Questions:
Should I use the zip operator or is there a better one to fit my cause?
It seems a bit messy doing it this way. This is only a part of the code, I have currently 12 dictionaries to load. Is there a way to refactor it?
I would like to insert a single dictionary data as soon as it finishes downloading and have a retry mechanism it the download fails. How can I achieve that?
I think in your case it's better to use Completable, because for you matter only tasks completion.
Completable getAndStoreOrderTypes = backendService.getOrderTypes()
.doOnNext(types -> *store to db*)
.toCompletable();
Completable getAndStoreOrderStatuses = backendService.getOrderStatuses()
.doOnNext(statuses -> *store to db*)
.toCompletable();
Completable getAndStoreOrderPriorities = backendService.getOrderPriorities()
.doOnNext(priorities -> *store to db*)
.toCompletable();
return Completable.merge(getAndStoreOrderTypes,
getAndStoreOrderStatuses,
getAndStoreOrderPriorities);
If you need serial execution - use Completable.concat() instead of merge()
a retry mechanism if the download fails
Use handy retry() operator
It is not good, to throw null value object into Rx Stream (in zip your return null, it is bad).
Try to not doing that.
In your case, you have 1 api call and 2 actions to save response into the database, so you can create the chain with flatMap.
It will look like:
backendService.getOrderTypes()
.doOnNext(savingToDatabaseLogic)
.flatMap(data -> mockBackendService.getOrderStatuses())
.doOnNext(...)
.flatMap(data -> backendService.getPriorities())
.doOnNext(...)
if you want to react on error situation, in particular, observable, you can add onErrorResumeNext(exception->Observable.empty()) and chain will continue even if something happened
Also, you can create something like BaseDao, which can save any Dao objects.

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