如何使 RxJava 调用 onErrorReturn() 并仍然执行 onError()

How do I make RxJava call onErrorReturn() and still execute onError()

当我从 MainViewModel 调用 Retrofit 方法 GetTodoRepository.fetchTodo() 并且调用以失败或任何非成功结果结束时,我想让 RxJava 都执行 onErrorReturn()onError() 所以我可以 return 在这种情况下缓存对象,但仍然通知 MainViewModel 发生了错误,因此我可以显示与错误相关的 UI 视图。我该如何存档?

当前代码显示了我打算如何处理它。

MainViewModel

public class MainViewModel extends ViewModel

    public LiveData<String> getTodo() {
    getTodoRepository.fetchTodo().subscribe(new SingleObserver<String>() {
        @Override
        public void onSubscribe(Disposable d) {
        }

        @Override
        public void onSuccess(String s) {
            showProgressAnim.setValue(false);
            todo.setValue(s);
        }

        @Override
        public void onError(Throwable e) {
            showProgressAnim.setValue(false);
            errorMsg.setValue(e.getMessage());
        }
     });
        return todo;
    }
}

GetTodoRepository

public class GetTodoRepository {

    public Single<String> fetchTodo() {
        return retrofit.create(TodoApi.class)
            .getTodo()
            .doOnSuccess(s -> cacheManager.saveTodo(s))
            .onErrorReturn(throwable -> cacheManager.getTodo())
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread());
    }
}

您不能同时使用 Single 的两种信号类型,但您可以将 fetchTodo() 转换为 Observable 并同时发出缓存项和错误:

fetchTodo()
.toObservable()
.onErrorResumeNext(error -> 
     Observable.just(cached)
     .concatWith(Observable.error(error))
)

我在评论中提到的第一种方法如下

为结果创建一个容器class

     class ToDoResult {

            boolean isCached;
            String todo;
            Throwable error; // this will be set only in case of error

            public ToDoResult(String todo, boolean isCached) {
                this.isCached = isCached;
                this.todo = todo;
            }

            public void setError(Throwable error) {
                this.error = error;
            }
        }

然后让你的 fetchTodo() return Single<ToDoResult> 而不是 Single<String> 如下

        public class GetTodoRepository {

            public Single<ToDoResult> fetchTodo() {
                return retrofit.create(TodoApi.class)
                    .getTodo()
                    .doOnSuccess(s -> cacheManager.saveTodo(s))
                    .map(todo -> new ToDoResult(todo,false))
                    .onErrorReturn(throwable -> {
                        ToDoResult toDoResult = new ToDoResult(cacheManager.getTodo(), true);
                        toDoResult.setError(throwable);
                        return toDoResult;
                    })
                    .subscribeOn(Schedulers.io())
                    .observeOn(AndroidSchedulers.mainThread());
            }
        }

在你的 ViewModel 中

     getTodoRepository.fetchTodo().subscribe(new SingleObserver<ToDoResult>() {
                @Override
                public void onSuccess(ToDoResult toDoResult) {
                    showProgressAnim.setValue(false);
                    if (toDoResult.error != null) {
                        errorMsg.setValue(toDoResult.error.getMessage());
                    } else {
                        todo.setValue(toDoResult.todo);
                    }

                }

                @Override
                public void onSubscribe(Disposable d) {

                }

                @Override
                public void onError(Throwable e) {
                    showProgressAnim.setValue(false);
                    errorMsg.setValue(e.getMessage());
                }
            });

在这种方法中,您的 onError 将永远不会被调用,因为我们总是将错误转换为成功信号。

第二种方法是按照@akarnokd 在之前的回答中提到的使用 Observable 并背靠背触发 onNextonError

 public class GetTodoRepository {

    public Observable<String> fetchTodo() {
        return  retrofit.create(TodoApi.class)
                        .getTodo()
                        .doOnSuccess(s -> cacheManager.saveTodo(s))
                        .toObservable()
                        .onErrorResumeNext(error ->
                                Observable.just(cached)
                                        .concatWith(Observable.error(error))
                        )
                        .subscribeOn(Schedulers.io())
                        .observeOn(AndroidSchedulers.mainThread());
    }

}

并像这样更改您的视图模型

   getTodoRepository.fetchTodo().subscribe(new Observer<String>() {
        @Override
        public void onSubscribe(Disposable d) {

        }

        @Override
        public void onNext(String s) {
             // this will be triggered with the todo item (cached in case of error)
        }

        @Override
        public void onError(Throwable e) {
           // this will be triggered followed by onNext in case of error
        }

        @Override
        public void onComplete() {

        }
    });