RxJava + retrofit, get a List and add extra info for each item
Asked Answered
A

2

5

I'm playing around with RXJava, retrofit in Android. I'm trying to accomplish the following:

I need to poll periodically a call that give me a Observable> (From here I could did it)

Once I get this list I want to iterate in each Delivery and call another methods that will give me the ETA (so just more info) I want to attach this new info into the delivery and give back the full list with the extra information attached to each item.

I know how to do that without rxjava once I get the list, but I would like to practice.

This is my code so far:

pollDeliveries = Observable.interval(POLLING_INTERVAL, TimeUnit.SECONDS, Schedulers.from(AsyncTask.THREAD_POOL_EXECUTOR))
            .map(tick -> RestClient.getInstance().getApiService().getDeliveries())
                    .doOnError(err -> Log.e("MPB", "Error retrieving messages" + err))
                    .retry()
                    .subscribe(deliveries -> {
                        MainApp.getEventBus().postSticky(deliveries);
                    });

This is giving me a list of deliveries. Now I would like to accomplish the second part.

Hope I been enough clear. Thanks

Algorithm answered 5/2, 2015 at 10:46 Comment(8)
Not sure, but you might be able to use flatmap to transform the deliveries into a new observable containing ETA's reactivex.io/documentation/operators/flatmap.htmlAbstract
Yes I saw some examples, but I could not really understand how to do it... it's the first time I'm trying to use rxjavaAlgorithm
Here is an example subscribing on GPS location, then transforming into address lookup Observable github.com/mcharmas/Android-ReactiveLocation/blob/master/sample/… line 90Abstract
@Abstract thank to share this resource, it's useful but not exactly my situation. He gets the list<address> and then he just take an address make it as string then send it to the view. I need to get for each item modify and return the full listAlgorithm
Not sure that I agree.. .flatMap receives a Location object (this could be a List<Something>), then inside .flatMap the Location is transformed into List<Address>, finally in .map the list of addresses is transformed into a string. If .map was excluded from the example, then .subscribe would have received List<Address>. So to sum up the events, the observer initial receives a Location, which is then transformed: Location -> flatMap(Location) -> List<Address> -> map(List<Address>) -> String -> subscribe(String).Abstract
You are right if we don't use the map() then we receive a list<Address> in the subscribe but it's not the situation I have. I need to iterate over the list get more info and then return the list with the extra info inside. In this example I get a location I make it a list and that was it. Do you see what I mean?Algorithm
Just dropping in here but why not .flatMap() the Delivery into ETA (so you iterate over the output in reactive style), and then toList() at the end so that the final output is an observable publishing a list of ETAs?Ludendorff
@AdamS could you provide an example? I'm a bit nob on this...Algorithm
A
4

Finally I found a nice way to do it.

private void startPolling() {
    pollDeliveries = Observable.interval(POLLING_INTERVAL, TimeUnit.SECONDS, Schedulers.from(AsyncTask.THREAD_POOL_EXECUTOR))
            .flatMap(tick -> getDeliveriesObs())
            .doOnError(err -> Log.e("MPB", "Error retrieving messages" + err))
            .retry()
            .subscribe(this::parseDeliveries, Throwable::printStackTrace);
}

private Observable<List<Delivery>> getDeliveriesObs() {
    return RestClient.getInstance().getApiService().getDeliveries()
            .flatMap(Observable::from)
            .flatMap(this::getETAForDelivery)
            .toSortedList((d1, d2) -> {
                if (d1.getEta() == null) {
                    return -1;
                }
                if (d2.getEta() == null) {
                    return 1;
                }
                return d1.getEta().getDuration().getValue() > d2.getEta().getDuration().getValue() ? 1 : -1;
            });
}

Let's go step by step.

  1. First we create an Observable that triggers every POLLING_INTERVAL time the method getDeliveriesObs() that will return the final list
  2. We use retrofit to get an Observable of the call
  3. We use flatMap to flattern the resut list and get in the next flatmap a Delivery item, one by one.
  4. Then we get the estimated time of arrival set inside the Delivery object and return it
  5. We sort the list to order by estimated time of arrival.
  6. In case of error we print and retry so the interval does not stop
  7. We subscribe finally to get the list sorted and with ETA inside, then we just return it or whatever you need to do with it.

It's working properly and it's quite nice, I'm starting to like rxjava :)

Algorithm answered 10/2, 2015 at 10:19 Comment(1)
I like this solution, and the step-by-step is nice too.Ludendorff
L
1

I haven't spent a lot of time with Java 8 lambdas, but here's an example of mapping each object to a different object, then getting a List<...> out at the other end in plain ol' Java 7:

List<Delivery> deliveries = ...;
Observable.from(deliveries).flatMap(new Func1<Delivery, Observable<ETA>>() {
    @Override
    public Observable<ETA> call(Delivery delivery) {
        // Convert delivery to ETA...
        return someEta;
    }
})
.toList().subscribe(new Action1<List<ETA>>() {
    @Override
    public void call(List<ETA> etas) {

    }
});

Of course, it'd be nice to take the Retrofit response (presumably an Observable<List<Delivery>>?) and just observe each of those. For that we ideally use something like flatten(), which doesn't appear to be coming to RxJava anytime soon.

To do that, you can instead do something like this (much nicer with lambdas). You'd replace Observable.from(deliveries) in the above example with the following:

apiService.getDeliveries().flatMap(new Func1<List<Delivery>, Observable<Delivery>>() {
    @Override
    public Observable<Delivery> call(List<Delivery> deliveries) {
        return Observable.from(deliveries);
    }
}).flatMap(...)
Ludendorff answered 5/2, 2015 at 15:35 Comment(1)
See my answer. I use your advice to made it. Thank you @Adam SAlgorithm

© 2022 - 2024 — McMap. All rights reserved.