链API调用中的RxJava错误处理

时间:2018-04-04 05:02:22

标签: java android rx-java2

我最近在玩Rxjava尝试实现一系列事件(Api callas /数据库操作),并且在处理错误时似乎遇到了障碍。

这就是我想要做的。我正在调用Api来检查数据库中是否存在用户。基于我得到的响应,我试图使用rxjava链接一些序列。下图可能会解释得更好。

                          checkUser()
                         /          \
                       No           Yes
                       /              \
            createUserRemote()       FetchUserNotesRemote()
                      |                    |
                    End               SaveUserNotesLocal()
                                            |
                                           End

我能够将checkUser() - >链接在一起FetchUserNotesRemote() - > SaveUserNotesLocal()序列,包含以下代码。

checkUser()
            .flatMap(id -> {return fetchData(id);})
            .flatMap(notesResponseObject -> {return saveFetchedData(notesResponseObject);})
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(new SingleObserver<Integer>() {
                @Override
                public void onSubscribe(Disposable d) {

                }

                @Override
                public void onSuccess(Integer integer) {
                    //handle onsuccess here
                }

                @Override
                public void onError(Throwable e) {
                    //handle errors here
                }
            });

我主要试图解决的问题。

  • 我无法弄清楚如何处理checkUser()返回
    的情况 404 http状态。因为当发生这种情况时,订户的onError为 方法被调用,在我看来应该发生什么。我怎么能够 处理它,以便当我从API得到错误(404)响应时, 而不是执行FetchUserNotesRemote()和SaveUserNotesLocal(), 我执行了不同的事件链?
  • 我不确定的另一件事是,如果有一个错误被调用 链中的任何可观察对象,订户的onError方法如何知道 哪个观察者叫它?

2 个答案:

答案 0 :(得分:6)

1)要在出错时执行不同的可观察链,可以使用方法onErorrResumeNext()。更多信息:github.com/ReactiveX/RxJava/wiki/Error-Handling-Operators

示例:

checkUser().flatMap(id -> {return fetchData(id);})
           .flatMap(notesResponseObject -> {return saveFetchedData(notesResponseObject);})
           .onErrorResumeNext(throwable -> { return doSomethingDifferent(); }
           .subscribeOn(Schedulers.io())
           .observeOn(AndroidSchedulers.mainThread())
           .subscribe(new SingleObserver<Integer>() {
                @Override
                public void onSubscribe(Disposable d) {

                }

                @Override
                public void onSuccess(Integer integer) {
                    //handle onsuccess here
                }

                @Override
                public void onError(Throwable e) {
                    //handle errors here
                }
            });

2)如果在流中的某处抛出异常,则会将其传递给订阅者onError()。如果您想知道抛出流错误的哪个部分,可以添加多个onErorrResumeNext()调用,这些调用会在每次api调用后抛出具体异常。

    checkUser()
           .onErrorResumeNext(throwable -> { return Observable.error(new CheckUserException()); }
           .flatMap(id -> {return fetchData(id);})
           .onErrorResumeNext(throwable -> { return Observable.error(new FetchDataException()); }
           .flatMap(notesResponseObject -> {return saveFetchedData(notesResponseObject);})
           .onErrorResumeNext(throwable -> { return Observable.error(new SaveDataException()); }
           .subscribeOn(Schedulers.io())
           .observeOn(AndroidSchedulers.mainThread())
           .subscribe(new SingleObserver<Integer>() {
                @Override
                public void onSubscribe(Disposable d) {

                }

                @Override
                public void onSuccess(Integer integer) {
                    //handle onsuccess here
                }

                @Override
                public void onError(Throwable e) {
                    //handle errors here
                }
            });

答案 1 :(得分:0)

我完全忘记了这一点。但是@mol将我推向正确的方向。我的解决方案有些不同。这可能不是最佳解决方案,但当时对我有用。

我首先创建了自己的自定义异常类,如下所示。

public class CreateUserLocalException extends Exception {
    public CreateUserLocalException(String message) {
        super(message);
    }
}

然后在我的checkUser()函数中,抛出上面创建的类型异常,如下所示。

public Single<String> checkUser(String id) {
    return Single.create(new SingleOnSubscribe<String>() {
        @Override
        public void subscribe(SingleEmitter<String> emitter) throws Exception {
            try {
                GetUserResponseObject getUserResponseObject = apiClient.usersIdGet(id);
                Log.d("Test", "checkUserCall: Status: " + getUserResponseObject.getStatus());
                emitter.onSuccess(getUserResponseObject.getBody().getUserId());
            } catch (AmazonServiceException e) {
                Log.d("Test", "AmazonServiceException : " + e.getErrorMessage());
                e.printStackTrace();
                if (e.getErrorMessage().equals("timeout")) {
                    throw new SocketTimeoutException();
                } else {
                    throw new CheckUserException(Integer.toString(e.getStatusCode()));
                }
            } catch (Exception e) {
                e.printStackTrace();
                throw new CheckUserException(Integer.toString(AppConstants.ERROR));
            }
        }
    });
}

然后在我的调用链中,如果发生错误,则在我检查Exception实例以识别发生哪种异常的地方调用onError(throwable)。下面是功能链的代码。

cloudSyncHelper.checkUser(user.getUser_id())
        .retry(3, new Predicate<Throwable>() {
            @Override
            public boolean test(Throwable throwable) throws Exception {
                Log.d("Test", throwable.toString());
                if (throwable instanceof SocketTimeoutException) {
                    Log.d("Test", "Time out.. Retrying..");
                    return true;
                }
                return false;
            }
        })
        .flatMap(s -> {
            return cloudSyncHelper.createUserLocal(user)
                    .onErrorResumeNext(throwable -> {
                        Log.d("Test", "onErrorResumeNext, throwable message: " + throwable.getMessage());
                        if (throwable instanceof CreateUserLocalException) {
                            if (Integer.parseInt(throwable.getMessage()) == AppConstants.LOCAL_DB_DUPLICATE) {
                                return Single.just(user.getUser_id());
                            }
                        }
                        return Single.error(new CreateUserLocalException(Integer.toString(AppConstants.LOCAL_DB_ERROR)));
                    });
        })
        .flatMap(id -> {
            return cloudSyncHelper.fetchData(id)
                    .retry(3, new Predicate<Throwable>() {
                        @Override
                        public boolean test(Throwable throwable) throws Exception {
                            Log.d("Test", throwable.toString());
                            if (throwable instanceof SocketTimeoutException) {
                                Log.d("Test", "Time out.. Retrying..");
                                return true;
                            }
                            return false;
                        }
                    });
        })
        .flatMap(notesResponseObject -> {
            return cloudSyncHelper.saveFetchedData(notesResponseObject);
        })
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(new SingleObserver<Integer>() {
            @Override
            public void onSubscribe(Disposable d) {
            }

            @Override
            public void onSuccess(Integer integer) {
                //handle onsuccess here
                googleSignInButton.setEnabled(true);
                progressBar.setVisibility(View.GONE);
                Log.d("Test", "onSuccess Called");
                getSharedPreferences(AppConstants.AppName, MODE_PRIVATE).edit().putBoolean("isFirstRun", false).apply();
                startActivity(new Intent(LoginScreen.this, HomeScreen.class));
            }

            @Override
            public void onError(Throwable e) {

                if (e instanceof SocketTimeoutException) {
                    googleSignInButton.setEnabled(true);
                    progressBar.setVisibility(View.GONE);
                    Log.d("Test", "Socket Time Out");
                    Utils.createToast(LoginScreen.this, "Socket timed out");
                    return;
                }

                int code = Integer.parseInt(e.getMessage());
                Log.d("Test", "onError Called");
                if (e instanceof CheckUserException) {
                    Log.d("Test", "onError CheckUserException");
                    if (code == AppConstants.NOTFOUND) {
                        newUserSequence(user);
                    } else {
                        googleSignInButton.setEnabled(true);
                        progressBar.setVisibility(View.GONE);
                        Utils.createToast(LoginScreen.this, "Unable to user information from cloud. Try again.");
                    }
                }
                if (e instanceof CreateUserLocalException) {
                    Log.d("Test", "onError CreateUserLocalException");
                    googleSignInButton.setEnabled(true);
                    progressBar.setVisibility(View.GONE);
                }
                if (e instanceof FetchDataException) {
                    Log.d("Test", "onError FetchDataException");
                    if (code == AppConstants.NOTFOUND) {
                        googleSignInButton.setEnabled(true);
                        progressBar.setVisibility(View.GONE);
                        getSharedPreferences(AppConstants.AppName, MODE_PRIVATE).edit().putBoolean("isFirstRun", false).apply();
                        startActivity(new Intent(LoginScreen.this, HomeScreen.class));
                    } else {
                        googleSignInButton.setEnabled(true);
                        progressBar.setVisibility(View.GONE);
                        Log.d("Test", "Unable to fetch data from cloud");
                        Utils.createToast(LoginScreen.this, "Unable to fetch data from cloud. Try again.");
                    }
                }
                if (e instanceof SaveDataLocalException) {
                    googleSignInButton.setEnabled(true);
                    progressBar.setVisibility(View.GONE);
                    Log.d("Test", "onError SaveDataLocalException");
                    if (code == AppConstants.LOCAL_DB_ERROR) {
                        Log.d("Test", "Unable to save data fetched from cloud");
                        Utils.createToast(LoginScreen.this, "Unable to save data fetched from cloud");
                    } else {
                        Utils.createToast(LoginScreen.this, "Unable to save data fetched from cloud");
                    }
                }
            }
        });

希望这会有所帮助。