Retrofit and RxJava: How to combine two requests and get access to both results?

18,518

Solution 1

As I understand - you need to make a request based on result of another request and combine both results. For that purpose you can use this flatMap operator variant: Observable.flatMap(Func1 collectionSelector, Func2 resultSelector)

Returns an Observable that emits the results of a specified function to the pair of values emitted by the source Observable and a specified collection Observable.enter image description here

Simple example to point you how to rewrite your code:

private Observable<String> makeRequestToServiceA() {
    return Observable.just("serviceA response"); //some network call
}

private Observable<String> makeRequestToServiceB(String serviceAResponse) {
    return Observable.just("serviceB response"); //some network call based on response from ServiceA
}

private void doTheJob() {
    makeRequestToServiceA()
            .flatMap(new Func1<String, Observable<? extends String>>() {
                @Override
                public Observable<? extends String> call(String responseFromServiceA) {
                    //make second request based on response from ServiceA
                    return makeRequestToServiceB(responseFromServiceA);
                }
            }, new Func2<String, String, Observable<String>>() {
                @Override
                public Observable<String> call(String responseFromServiceA, String responseFromServiceB) {
                    //combine results
                    return Observable.just("here is combined result!");
                }
            })
            //apply schedulers, subscribe etc
}

Using lambdas:

private void doTheJob() {
    makeRequestToServiceA()
            .flatMap(responseFromServiceA -> makeRequestToServiceB(responseFromServiceA),
                    (responseFromServiceA, responseFromServiceB) -> Observable.just("here is combined result!"))
            //...
}

Solution 2

The operator you are looking for is flatMap()

serviceA.getAllGeneros("movie","list","da0d692f7f62a1dc687580f79dc1e6a0")
    .flatMap(genres -> serviceB.getAllMovies(genres.getId() ......))
Share:
18,518

Related videos on Youtube

David Hackro
Author by

David Hackro

https://github.com/David-Hackro https://github.com/TutorialesHackro https://www.reddit.com/user/hackro/ https://www.youtube.com/user/tutorialeshackro

Updated on June 04, 2022

Comments

  • David Hackro
    David Hackro almost 2 years

    I need to make two requests for services and combine it results:

    ServiceA() => [{"id":1,"name":"title"},{"id":1,"name":"title"}]

    ServiceB(id) => {"field":"value","field1":"value"}

    Currently, I have managed to combine the results, but I need to pass id as a parameter to the ServiceB and get access to the first result.

    What I tried so far:

       Retrofit repo = new Retrofit.Builder()
                    .baseUrl("https://api.themoviedb.org/3/genre/")
                    .addConverterFactory(GsonConverterFactory.create())
                    .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                    .build();
    
            Observable<GenerosResponse> Genres  = repo
                    .create(services.class)
                    .getAllGeneros("movie","list","da0d692f7f62a1dc687580f79dc1e6a0")
                    .subscribeOn(Schedulers.newThread())
                    .observeOn(AndroidSchedulers.mainThread());
    
            Observable<ResponseMovies> Movies = repo
                    .create(services.class)
                    .getAllMovies("28","movies","da0d692f7f62a1dc687580f79dc1e6a0",12)
                    .subscribeOn(Schedulers.newThread())
                    .observeOn(AndroidSchedulers.mainThread());
    
            Observable<CollectionsMovies> combined = Observable.zip(Genres, Movies, new Func2<GenerosResponse, ResponseMovies, CollectionsMovies>() {
                @Override
                public CollectionsMovies call(GenerosResponse generosResponse, ResponseMovies responseMovies) {
                    return new CollectionsMovies(generosResponse, responseMovies);
                }
            });
    
            combined.
                    subscribeOn(Schedulers.newThread())
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribe(...);
    

    Edit

    Solution according to @Maxim Ostrovidov's answer:

     private Observable<GenerosResponse> makeRequestToServiceA() {
            return  service.getAllGeneros("movie","list","da0d692f7f62a1dc687580f79dc1e6a0"); //some network call
        }
    
        private Observable<ResponseMovies> makeRequestToServiceB(Genre genre) {
            return service.getAllMovies(genre.getId(),"movies","da0d692f7f62a1dc687580f79dc1e6a0","created_at.asc"); //some network call based on response from ServiceA
        }
    
        void doTheJob() {
    
            makeRequestToServiceA()
            .flatMap(userResponse -> Observable.just(userResponse.getGenres()))      //get list from response
                    .flatMapIterable(baseDatas -> baseDatas)
                    .flatMap(new Func1<Genre, Observable<? extends ResponseMovies>>() {
    
                        @Override
                        public Observable<? extends ResponseMovies> call(Genre genre) {
                            return makeRequestToServiceB(genre);
                        }
                    }, new Func2<Genre, ResponseMovies, CollectionsMovies>() {
    
                        @Override
                        public CollectionsMovies call(Genre genre, ResponseMovies responseMovies) {
                            return new CollectionsMovies(genre,responseMovies);
                        }
                    }).
                    subscribeOn(Schedulers.newThread())
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribe(....);
        }
    
    • LordRaydenMK
      LordRaydenMK over 7 years
      You need to call ServiceB with parameters from the result of the call to ServiceA?
    • David Hackro
      David Hackro over 7 years
      yes exactly @LordRaydenMK
  • David Hackro
    David Hackro over 7 years
    but if my service return list objects and my service need id the each for object?
  • Maksim Ostrovidov
    Maksim Ostrovidov over 7 years
    Please provide code of getAllGeneros and getAllMovies methods.
  • Maksim Ostrovidov
    Maksim Ostrovidov over 7 years
    I can't understand what problem you are trying to solve with your code. Can you please provide a common explanation, e.g. "I am trying to show movies by given genres, passing genre id to..."
  • David Hackro
    David Hackro over 7 years
    yes, i need passing id to getAllMovies() and combine Object Genre whit ResponseMovies, it's posible?
  • Maksim Ostrovidov
    Maksim Ostrovidov over 7 years
    So, according to my example - in makeRequestToServiceB you need to pass Genre ID from first response and make second request.
  • Maksim Ostrovidov
    Maksim Ostrovidov over 7 years
    To achieve that all your code must be rewritten, so please take time to analyze my example.
  • David Hackro
    David Hackro over 7 years
    thanks! work for me,please a favor,you can verify if my implementation it's correct? i updated the question whit the answerd
  • Maksim Ostrovidov
    Maksim Ostrovidov over 7 years
    I checked your solution and it's correct. Glad to be helpful!
  • BabyishTank
    BabyishTank over 3 years
    Hi, does this work if I have 2 or more observable?