在RxJava中将onErrorReturn与retryWhen一起使用

时间:2019-10-02 15:18:33

标签: java reactive-programming rx-java2

代码如下:

import io.reactivex.Observable;
import io.reactivex.Observer;
import org.junit.jupiter.api.Test;

import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.atomic.AtomicBoolean;

public class RxJavaTest {

    @Test
    public void onErr() {

        Observable<String> values1 = new Observable<String>() {
            @Override
            protected void subscribeActual(Observer<? super String> observer) {
                observer.onNext("New");
                observer.onNext("New1");
                observer.onNext("New2");
                observer.onNext("New3");
                observer.onNext("New4");
                if (ThreadLocalRandom
                            .current()
                            .nextInt(10) == 5) {
                    observer.onError(new Exception("don't retry..."));
                } else {
                    observer.onError(new IllegalArgumentException("retry..."));
                }
            }
        };
        AtomicBoolean finished = new AtomicBoolean(false);
        values1
                .retryWhen(throwableObservable -> throwableObservable
                        .takeWhile(throwable -> {
                            boolean result = (throwable instanceof IllegalArgumentException);
                            if (result) {
                                System.out.println("Retry on error: " + throwable);
                                return result;
                            }
                            System.out.println("Error: " + throwable);
                            return result;
                        })
                        .take(20))
                .onErrorReturn(throwable -> "Saved the day!")
                .doOnTerminate(() -> finished.set(true))
                .subscribe(v -> System.out.println(v));
    }
}

目标是

  • 仅在有IllegalArgumentException
  • 时重试
  • 对于其他任何异常,请立即返回(通过onErrorReturn)。

上面的代码完成了第一个目标,但在第二个目标上失败了,它停止了重试,但是忽略了.onErrorReturn部分。

有什么主意吗?

2 个答案:

答案 0 :(得分:1)

您可以将retryWhen更改为:

                .retryWhen(throwableObservable ->
                                throwableObservable.flatMap(throwable -> {
                                    if (throwable instanceof IllegalArgumentException) {
                                        System.out.println("Retry on error: " + throwable);
                                        return Observable.just(1);
                                    } else {
                                        System.out.println("Error: " + throwable);
                                        return Observable.<Integer>error(throwable);
                                    }
                                })
                )

为了使其重试,在retryWhen中返回哪个值都无关紧要(在上面的示例中,它返回1)。按照javadoc:

  

如果ObservableSource调用onComplete或onError,则重试将在子订阅上调用onComplete或onError。否则,此ObservableSource将重新订阅源ObservableSource。

答案 1 :(得分:0)

记录下来,这是我在使用onErrorResumeNext看到古斯塔沃的答案之前的解决方案:

    private Observable<String> createObservable(long delay) {
        Observable<String> values1 = new Observable<String>() {
            @Override
            protected void subscribeActual(Observer<? super String> observer) {
                observer.onNext("New");
                observer.onNext("New1");
                observer.onNext("New2");
                observer.onNext("New3");
                observer.onNext("New4");
                if (ThreadLocalRandom
                        .current()
                        .nextInt(8) == 2) {
                    observer.onError(new RuntimeException("don't retry..."));
                } else {
                    observer.onError(new IllegalArgumentException("retry..."));
                }
            }
        };
        return Observable.timer(delay, TimeUnit.SECONDS).flatMap(aLong -> values1)
                .onErrorResumeNext((Throwable throwable) -> {
                    if (throwable instanceof IllegalArgumentException) {
                        return createObservable(delay + 2);
                    } else {
                        return Observable.just("The default value");
                    }
                });
    }

这按预期工作,但我认为Gustavo建议的方式更容易理解。这是使用retryWhen重写的同一函数:

    private Observable<String> createObservable1() {
        Observable<String> values1 = new Observable<String>() {
            @Override
            protected void subscribeActual(Observer<? super String> observer) {
                observer.onNext("New");
                observer.onNext("New1");
                observer.onNext("New2");
                observer.onNext("New3");
                observer.onNext("New4");
                if (ThreadLocalRandom
                        .current()
                        .nextInt(3) == 1) {
                    observer.onError(new RuntimeException("don't retry..."));
                } else {
                    observer.onError(new IllegalArgumentException("retry..."));
                }
            }
        };
        return values1.retryWhen(throwableObservable ->
                throwableObservable
                        .zipWith(Observable.range(1, 5), (throwable, integer) -> {
                            if (throwable instanceof IllegalArgumentException) {
                                System.out.println("Retry on error: " + throwable);
                                return integer;
                            }
                            System.out.println("No retry on error: " + throwable);
                            return -1;
                        })
                        .flatMap(integer -> {
                            if (integer > 0) {
                                System.out.println("Delay " + integer + " sec on retry...");
                                return Observable.timer(integer, TimeUnit.SECONDS);
                            }
                            System.out.println("Return immediately...");
                            return Observable.error(new Exception());
                        })
        ).onErrorReturnItem("Saved the day!");
    }

希望这会有所帮助。