rxjava: Can I use retry() but with delay?

后端 未结 14 2209
别那么骄傲
别那么骄傲 2020-11-28 18:25

I am using rxjava in my Android app to handle network requests asynchronously. Now I would like to retry a failed network request only after a certain time has passed.

相关标签:
14条回答
  • 2020-11-28 18:54

    Same answer as from kjones but updated to latest version For RxJava 2.x version: ('io.reactivex.rxjava2:rxjava:2.1.3')

    public class RetryWithDelay implements Function<Flowable<Throwable>, Publisher<?>> {
    
        private final int maxRetries;
        private final long retryDelayMillis;
        private int retryCount;
    
        public RetryWithDelay(final int maxRetries, final int retryDelayMillis) {
            this.maxRetries = maxRetries;
            this.retryDelayMillis = retryDelayMillis;
            this.retryCount = 0;
        }
    
        @Override
        public Publisher<?> apply(Flowable<Throwable> throwableFlowable) throws Exception {
            return throwableFlowable.flatMap(new Function<Throwable, Publisher<?>>() {
                @Override
                public Publisher<?> apply(Throwable throwable) throws Exception {
                    if (++retryCount < maxRetries) {
                        // When this Observable calls onNext, the original
                        // Observable will be retried (i.e. re-subscribed).
                        return Flowable.timer(retryDelayMillis,
                                TimeUnit.MILLISECONDS);
                    }
    
                    // Max retries hit. Just pass the error along.
                    return Flowable.error(throwable);
                }
            });
        }
    }
    

    Usage:

    // Add retry logic to existing observable. // Retry max of 3 times with a delay of 2 seconds.

    observable
        .retryWhen(new RetryWithDelay(3, 2000));
    
    0 讨论(0)
  • 2020-11-28 18:55

    Inspired by Paul's answer, and if you are not concerned with retryWhen problems stated by Abhijit Sarkar, the simplest way to delay resubscription with rxJava2 unconditionnaly is :

    source.retryWhen(throwables -> throwables.delay(1, TimeUnit.SECONDS))
    

    You may want to see more samples and explanations on retryWhen and repeatWhen.

    0 讨论(0)
  • 2020-11-28 18:57

    Now with RxJava version 1.0+ you can use zipWith to achieve retry with delay.

    Adding modifications to kjones answer.

    Modified

    public class RetryWithDelay implements 
                                Func1<Observable<? extends Throwable>, Observable<?>> {
    
        private final int MAX_RETRIES;
        private final int DELAY_DURATION;
        private final int START_RETRY;
    
        /**
         * Provide number of retries and seconds to be delayed between retry.
         *
         * @param maxRetries             Number of retries.
         * @param delayDurationInSeconds Seconds to be delays in each retry.
         */
        public RetryWithDelay(int maxRetries, int delayDurationInSeconds) {
            MAX_RETRIES = maxRetries;
            DELAY_DURATION = delayDurationInSeconds;
            START_RETRY = 1;
        }
    
        @Override
        public Observable<?> call(Observable<? extends Throwable> observable) {
            return observable
                    .delay(DELAY_DURATION, TimeUnit.SECONDS)
                    .zipWith(Observable.range(START_RETRY, MAX_RETRIES), 
                             new Func2<Throwable, Integer, Integer>() {
                                 @Override
                                 public Integer call(Throwable throwable, Integer attempt) {
                                      return attempt;
                                 }
                             });
        }
    }
    
    0 讨论(0)
  • 2020-11-28 18:57

    (Kotlin) I little bit improved code with exponential backoff and applied defense emitting of Observable.range():

        fun testOnRetryWithDelayExponentialBackoff() {
        val interval = 1
        val maxCount = 3
        val ai = AtomicInteger(1);
        val source = Observable.create<Unit> { emitter ->
            val attempt = ai.getAndIncrement()
            println("Subscribe ${attempt}")
            if (attempt >= maxCount) {
                emitter.onNext(Unit)
                emitter.onComplete()
            }
            emitter.onError(RuntimeException("Test $attempt"))
        }
    
        // Below implementation of "retryWhen" function, remove all "println()" for real code.
        val sourceWithRetry: Observable<Unit> = source.retryWhen { throwableRx ->
            throwableRx.doOnNext({ println("Error: $it") })
                    .zipWith(Observable.range(1, maxCount)
                            .concatMap { Observable.just(it).delay(0, TimeUnit.MILLISECONDS) },
                            BiFunction { t1: Throwable, t2: Int -> t1 to t2 }
                    )
                    .flatMap { pair ->
                        if (pair.second >= maxCount) {
                            Observable.error(pair.first)
                        } else {
                            val delay = interval * 2F.pow(pair.second)
                            println("retry delay: $delay")
                            Observable.timer(delay.toLong(), TimeUnit.SECONDS)
                        }
                    }
        }
    
        //Code to print the result in terminal.
        sourceWithRetry
                .doOnComplete { println("Complete") }
                .doOnError({ println("Final Error: $it") })
                .blockingForEach { println("$it") }
    }
    
    0 讨论(0)
  • 2020-11-28 18:59

    This is a solution based on Ben Christensen's snippets I saw, RetryWhen Example, and RetryWhenTestsConditional (I had to change n.getThrowable() to n for it to work). I used evant/gradle-retrolambda to make the lambda notation work on Android, but you don't have to use lambdas (although it's highly recommended). For the delay I implemented exponential back-off, but you can plug in what ever backoff logic you want there. For completeness I added the subscribeOn and observeOn operators. I'm using ReactiveX/RxAndroid for the AndroidSchedulers.mainThread().

    int ATTEMPT_COUNT = 10;
    
    public class Tuple<X, Y> {
        public final X x;
        public final Y y;
    
        public Tuple(X x, Y y) {
            this.x = x;
            this.y = y;
        }
    }
    
    
    observable
        .subscribeOn(Schedulers.io())
        .retryWhen(
                attempts -> {
                    return attempts.zipWith(Observable.range(1, ATTEMPT_COUNT + 1), (n, i) -> new Tuple<Throwable, Integer>(n, i))
                    .flatMap(
                            ni -> {
                                if (ni.y > ATTEMPT_COUNT)
                                    return Observable.error(ni.x);
                                return Observable.timer((long) Math.pow(2, ni.y), TimeUnit.SECONDS);
                            });
                })
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(subscriber);
    
    0 讨论(0)
  • 2020-11-28 19:02

    You can use the retryWhen() operator to add retry logic to any Observable.

    The following class contains the retry logic:

    RxJava 2.x

    public class RetryWithDelay implements Function<Observable<? extends Throwable>, Observable<?>> {
        private final int maxRetries;
        private final int retryDelayMillis;
        private int retryCount;
    
        public RetryWithDelay(final int maxRetries, final int retryDelayMillis) {
            this.maxRetries = maxRetries;
            this.retryDelayMillis = retryDelayMillis;
            this.retryCount = 0;
        }
    
        @Override
        public Observable<?> apply(final Observable<? extends Throwable> attempts) {
            return attempts
                    .flatMap(new Function<Throwable, Observable<?>>() {
                        @Override
                        public Observable<?> apply(final Throwable throwable) {
                            if (++retryCount < maxRetries) {
                                // When this Observable calls onNext, the original
                                // Observable will be retried (i.e. re-subscribed).
                                return Observable.timer(retryDelayMillis,
                                        TimeUnit.MILLISECONDS);
                            }
    
                            // Max retries hit. Just pass the error along.
                            return Observable.error(throwable);
                        }
                    });
        }
    }
    

    RxJava 1.x

    public class RetryWithDelay implements
            Func1<Observable<? extends Throwable>, Observable<?>> {
    
        private final int maxRetries;
        private final int retryDelayMillis;
        private int retryCount;
    
        public RetryWithDelay(final int maxRetries, final int retryDelayMillis) {
            this.maxRetries = maxRetries;
            this.retryDelayMillis = retryDelayMillis;
            this.retryCount = 0;
        }
    
        @Override
        public Observable<?> call(Observable<? extends Throwable> attempts) {
            return attempts
                    .flatMap(new Func1<Throwable, Observable<?>>() {
                        @Override
                        public Observable<?> call(Throwable throwable) {
                            if (++retryCount < maxRetries) {
                                // When this Observable calls onNext, the original
                                // Observable will be retried (i.e. re-subscribed).
                                return Observable.timer(retryDelayMillis,
                                        TimeUnit.MILLISECONDS);
                            }
    
                            // Max retries hit. Just pass the error along.
                            return Observable.error(throwable);
                        }
                    });
        }
    }
    

    Usage:

    // Add retry logic to existing observable.
    // Retry max of 3 times with a delay of 2 seconds.
    observable
        .retryWhen(new RetryWithDelay(3, 2000));
    
    0 讨论(0)
提交回复
热议问题