【问题标题】:How do I make RxJava call onErrorReturn() and still execute onError()如何让 RxJava 调用 onErrorReturn() 并仍然执行 onError()
【发布时间】:2020-05-13 08:09:58
【问题描述】:

当我从MainViewModel 调用Retrofit 方法GetTodoRepository.fetchTodo() 并且调用以失败或任何不成功的结果结束时,我想让RxJava 同时执行onErrorReturn()onError(),这样我就可以返回一个在这种情况下缓存对象,但仍然通知 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());
    }
}

【问题讨论】:

  • 您是否要同时在 ViewModel 中调用 onSuccessonError?如果是这样,那是不可能的。 onSuccessonError 是终端事件,互斥。您可以将错误视为不同的成功状态并将其传递给onSuccess。您可以返回一个对象,该对象将包含您从存储库返回的 String 和一个表示它是否来自缓存的标志。这样即使 API 失败,您也将始终获得成功回调。在 onSuccess 中,您可以通过 result.isFromCache() 之类的操作来检查它
  • @SarathKn 是的,我两个都打电话。我用普通的 Java 监听器做到了这一点,之前你可以在 GET 请求失败的情况下以任何顺序或方式调用 listener.onSuccess(String msg)listener.onError(String msg)。我将如何返回一个 RxJava 标志,指示返回值 isFromCache()
  • 您甚至可以使用@akarnokd 建议的方法。我会用这两种方法添加一个答案

标签: java android retrofit rx-java rx-java2


【解决方案1】:

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

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

【讨论】:

    【解决方案2】:

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

    为结果创建一个持有者类

         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() 返回Single&lt;ToDoResult&gt; 而不是Single&lt;String&gt;,如下所示

            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() {
    
            }
        });
    

    【讨论】:

    • 谢谢!我使用了第二种方法,结果就是我想要的
    猜你喜欢
    • 1970-01-01
    • 2020-09-22
    • 1970-01-01
    • 2018-02-20
    • 2017-03-30
    • 1970-01-01
    • 2017-05-09
    • 2019-09-06
    • 1970-01-01
    相关资源
    最近更新 更多