简体   繁体   中英

Chain two retrofit observables w/ RxJava

I want to execute 2 network calls one after another. Both network calls return Observable. Second call uses data from successful result of the first call, method in successful result of second call uses data from both successful result of the first and of the second call. Also i should be able to handle both onError "events" differently. How can i achieve this avoiding callback hell like in example below:

       API().auth(email, password)
            .subscribeOn(Schedulers.newThread())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(new Action1<AuthResponse>() {
                @Override
                public void call(final AuthResponse authResponse) {
                    API().getUser(authResponse.getAccessToken())
                            .subscribe(new Action1<List<User>>() {
                                @Override
                                public void call(List<User> users) {
                                    doSomething(authResponse, users);
                                }
                            }, new Action1<Throwable>() {
                                @Override
                                public void call(Throwable throwable) {
                                    onErrorGetUser();
                                }
                            });
                }
            }, new Action1<Throwable>() {
                @Override
                public void call(Throwable throwable) {
                    onErrorAuth();
                }
            });

I know about zip, but i want to avoid creating "Combiner class".

Update 1. Tried to implement akarnokd's answer:

         API()
            .auth(email, password)
            .subscribeOn(Schedulers.newThread())
            .observeOn(AndroidSchedulers.mainThread())
            .flatMap(authResponse -> API()
                    .getUser(authResponse.getAccessToken())
                    .doOnError(throwable -> {
                        getView().setError(processFail(throwable));
                    }), ((authResponse, users) -> {
                // Ensure returned user is the which was authenticated
                if (authResponse.getUserId().equals(users.get(0).getId())) {
                    SessionManager.getInstance().initSession(email, password, authResponse.getAccessToken(), users.get(0));
                    getView().toNews();
                } else {
                    getView().setError(R.string.something_went_wrong);
                }
            }));

However inside flatMap method compiler says it can't resolve methods of authResponse and users ( authResponse.getAccessToken() , users.get(0) etc). Im new to rx programming and lambdas - please tell me what's the problem. Anyway code looks much cleaner now.

Update 2.

API()
            .auth(email, password)
            .subscribeOn(Schedulers.newThread())
            .observeOn(AndroidSchedulers.mainThread())
            .doOnError(throwable -> getView().setError(processFail(throwable)))
            .flatMap((AuthResponse authResponse) -> API()
                    .getUser(authResponse.getAccessToken())
                    .doOnError(throwable -> getView().setError(processFail(throwable))), ((AuthResponse authResponse, List<User> users) -> {
                            // Ensure returned user is the which was authenticated
                            if (authResponse.getUserId().equals(users.get(0).getId())) {
                                SessionManager.getInstance().initSession(email, password, authResponse.getAccessToken(), users.get(0));
                                getView().toNews();
                            }
                            return Observable.just(this);
            }));

Have done it like this, but now my network calls aren't executing at all.

Have you looked into flatMap()? If your aversion to it (or zip()) is the need to make an unnecessary class just to hold two objects, android.util.Pair might be an answer. I'm not sure how to get exactly the error handling you're looking for, though.

       API().auth(email, password)
        .subscribeOn(Schedulers.newThread())
        .observeOn(AndroidSchedulers.mainThread())
        .flatMap(new Func1<AuthResponse, Observable<List<User>>>() {
          @Override
          public Observable<List<User>> call(AuthResponse authResponse) {
            return API().getUser(authResponse.getAccessToken());
          }
        }, new Func2<AuthResponse, List<User>, Pair<AuthResponse, List<User>>>() {
          @Override
          public Pair<AuthResponse, List<User>> call(AuthResponse authResponse, List<User> users) {
            return new Pair<>(authResponse, users);
          }
        }).subscribe(new Action1<Pair<AuthResponse, List<User>>>() {
          @Override
          public void call(Pair<AuthResponse, List<User>> pair) {
            doSomething(pair.first, pair.second);
          }
        }, new Action1<Throwable>() {
          @Override
          public void call(Throwable throwable) {
            // not sure how to tell which one threw the error
          }
        });

In addition to Anthony R.'s answer, there is a flatMap overload which takes a Func2 and pairs your primary and flattened values for you. In addition, look at the onErrorXXX and onExceptionXXX operators for error manipulation, and chain them with your first and second Observables

first.onErrorReturn(1)
.flatMap(v -> service(v).onErrorReturn(2), (a, b) -> a + b);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM