Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Concat multiple calls with Retrofit 2 and rxJava

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

like image 980
FVod Avatar asked Jan 06 '23 02:01

FVod


2 Answers

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)
like image 162
R. Zagórski Avatar answered Jan 13 '23 12:01

R. Zagórski


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
       });
like image 26
Maksim Ostrovidov Avatar answered Jan 13 '23 12:01

Maksim Ostrovidov