Как заставить Rx Java вызывать onErrorReturn () и по-прежнему выполнять onError () - PullRequest
0 голосов
/ 28 января 2020

Когда я вызываю метод Retrofit GetTodoRepository.fetchTodo() из MainViewModel и вызов заканчивается неудачей или каким-либо неуспешным результатом, я бы хотел, чтобы Rx Java одновременно выполнял onErrorReturn() и onError(), поэтому я в этом случае может вернуть кешированный объект, но все равно уведомить MainViewModel, что произошла ошибка, поэтому я могу показать связанные с ошибками представления пользовательского интерфейса. Как мне заархивировать это?

Текущий код показывает, как я намеревался справиться с этим.

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

Ответы [ 2 ]

2 голосов
/ 28 января 2020

Вы не можете иметь оба типа сигналов с Single, но вы можете превратить fetchTodo() в Observable и создать кешированный элемент и ошибку вместе:

fetchTodo()
.toObservable()
.onErrorResumeNext(error -> 
     Observable.just(cached)
     .concatWith(Observable.error(error))
)
1 голос
/ 28 января 2020

Первый подход, который я упомянул в комментарии, следующий:

создайте класс держателя для результата

     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<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 и запуске onNext и onError спина к спине.

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

        }
    });
...