RXJava2: correct pattern to chain retrofit request

2020-03-01 09:13发布

问题:

I am relatively new to RXJava in general (really only started using it with RXJava2), and most documentation I can find tends to be RXJava1; I can usually translate between both now, but the entire Reactive stuff is so big, that it's an overwhelming API with good documentation (when you can find it). I'm trying to streamline my code, an I want to do it with baby steps. The first problem I want to solve is this common pattern I do a lot in my current project:

You have a Request that, if successful, you will use to make a second request.

If either fails, you need to be able to identify which one failed. (mostly to display custom UI alerts).

This is how I usually do it right now:

(omitted the .subscribeOn/observeOn for simplicity)

Single<FirstResponse> first = retrofitService.getSomething();

first
   .subscribeWith(
     new DisposableSingleObserver<FirstResponse>() {
         @Override
         public void onSuccess(final FirstResponse firstResponse) {

               // If FirstResponse is OK…
                Single<SecondResponse> second = 
                 retrofitService
                    .getSecondResponse(firstResponse.id) //value from 1st
                    .subscribeWith(
                      new DisposableSingleObserver<SecondResponse>() {

                           @Override
                           public void onSuccess(final SecondResponse secondResponse) {
                              // we're done with both!
                           }

                           @Override
                            public void onError(final Throwable error) {
                            //2nd request Failed, 
                            }                        
                     });

         }

         @Override
         public void onError(final Throwable error) {
              //firstRequest Failed, 
         }
      });

Is there a better way to deal with this in RXJava2?

I've tried flatMap and variations and even a Single.zip or similar, but I'm not sure what the easiest and most common pattern is to deal with this.

In case you're wondering FirstRequest will fetch an actual Token I need in the SecondRequest. Can't make second request without the token.

回答1:

I would suggest using flat map (And retrolambda if that is an option). Also you do not need to keep the return value (e.g Single<FirstResponse> first) if you are not doing anything with it.

retrofitService.getSomething()
    .flatMap(firstResponse -> retrofitService.getSecondResponse(firstResponse.id)
    .subscribeWith(new DisposableSingleObserver<SecondResponse>() {
         @Override
         public void onSuccess(final SecondResponse secondResponse) {
            // we're done with both!
         }

         @Override
          public void onError(final Throwable error) {
             // a request request Failed, 
          }                        
   });

This article helped me think through styles in how I structure RxJava in general. You want your chain to be a list of high level actions if possible so it can be read as a sequence of actions/transformations.

EDIT Without lambdas you can just use a Func1 for your flatMap. Does the same thing just a lot more boiler-plate code.

retrofitService.getSomething()
    .flatMap(new Func1<FirstResponse, Observable<SecondResponse> {
        public void Observable<SecondResponse> call(FirstResponse firstResponse) {
            return retrofitService.getSecondResponse(firstResponse.id)
        }
    })
    .subscribeWith(new DisposableSingleObserver<SecondResponse>() {
         @Override
         public void onSuccess(final SecondResponse secondResponse) {
            // we're done with both!
         }

         @Override
          public void onError(final Throwable error) {
             // a request request Failed, 
          }                        
   }); 


回答2:

Does this not work for you?

retrofitService
.getSomething()
.flatMap(firstResponse -> retrofitService.getSecondResponse(firstResponse.id))
.doOnNext(secondResponse -> {/* both requests succeeded */})
/* do more stuff with the response, or just subscribe */