3
votes

I need to concat multiple API calls and I'd like to use retrofit2 and rxJava observables to do that. My scenario is as follows:

I must do a first call that will return an array of objects, the response will be something like:

[{"title": "Title 1", "id": "1"}, {"title": "Title 2", "id": "2"}]

Then I need to do an API call per each object, so each object's API response will be something like:

[{"title": "Chapter A", "id": "1A", ...}, {"title": "Chapter B", "id": "1B", ...}, ...]

So I want to concat all those calls and merge it in a unique response object that would have all information. How could I do that? How can I do the first call then with the response do a call per object and wait till all calls have been done and merge the results?

Thanks a lot

2
use zip or combileLast from RxJava - Blackbelt
Thank you very much, but how could I do that? I mean, how can I receive the result of the first observable and then create the others observables and combine its results? I guess that com zip would be used once I have the list of observables, but how can I do the first call, parse the response to create the list of observables? - FVod

2 Answers

5
votes

Suppose you have objects with defined properties:

public class TvShow {
    public String title;
    public Long id;
    public List<TvChapter> chapterList;
}

public class TvChapter {
    public String title;
    public String id;
}

And you already have methods to get data:

public Observable<List<TvShow>> getShows()

public Observable<List<TvChapter>> getTvChapters(Long tvShowId)

Here is the combined RxJava stream that returns Observable<List<TvShow>>:

getShows()
        .flatMap(new Func1<List<TvShow>, Observable<TvShow>>() {
            @Override
            public Observable<TvShow> call(List<TvShow> tvShows) {
                return Observable.from(tvShows);
            }
        }).flatMap(new Func1<TvShow, Observable<TvShow>>() {
    @Override
    public Observable<TvShow> call(TvShow tvShow) {
        return Observable.zip(Observable.just(tvShow),
                getTvChapters(tvShow.id),
                new Func2<TvShow, List<TvChapter>, TvShow>() {
                    @Override
                    public TvShow call(TvShow tvShow, List<TvChapter> tvChapters) {
                        tvShow.chapterList = tvChapters;
                        return tvShow;
                    }
                });
    }
}).toList();

The idea is to:

  1. Fetch all shows (getShows)
  2. Get a stream of single shows from list (Observable.from() operator)
  3. Get chapters for each show (getTvChapters(tvShow.id))
  4. Combine results of tvChapters call with tvShow
  5. Merge all results (Observable.toList() operator)
3
votes

using lambdas

getTitlesList() //first API call that returns Observable<Titles>
      .flatmap(titles -> Observable.from(titles)) //forcing list to emit each element separately
      .flatmap(title -> getDetail(title)) //second API call that returns Observable<Detail>
      .toList() //collect all emitted items to list
      .subscribeOn(Schedulers.io()) // don't forget to apply async scheduler,
                                    // otherwise you'll get *android.os.NetworkOnMainThreadException*
      .observeOn(AndroidSchedulers.mainThread()) //and switch thread back to main, if needed
      .subscribe(details -> {
           //process your details list
       }, e -> {
           //error handling
       });