Search code examples
kotlinrx-java2

Exponential backoff in Kotlin with RxJava2


I am trying to do an exponential retry for a request, so that if the request fails (i.e. your internet is down) the app retries endlessly until it works (for the amount of time the app is in the foreground)

I tried with this solution

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

But when I try to convert this to kotlin it says Function only takes one generic parameter.


Solution

  • I copy-pasted the Java code into IntelliJ and it did half the work for me:

    import java.util.concurrent.TimeUnit
    import io.reactivex.functions.Function
    import io.reactivex.*
    
    class RetryWithDelay(private val maxRetries: Int, private val retryDelayMillis: Long) : Function<Observable<Throwable>, Observable<Long>> {
    
        override fun apply(attempts: Observable<Throwable>): Observable<Long> {
            return attempts
                    .flatMap(object : Function<Throwable, Observable<Long>> {
    
                        private var retryCount: Int = 0
    
                        override fun apply(throwable: Throwable): Observable<Long> {
                            return if (++retryCount < maxRetries) {
                                // When this Observable calls onNext, the original
                                // Observable will be retried (i.e. re-subscribed).
                                Observable.timer(retryDelayMillis,
                                        TimeUnit.MILLISECONDS)
                            } else Observable.error<Long>(throwable)
    
                            // Max retries hit. Just pass the error along.
                        }
                    })
        }
    }
    

    Note that the retryCount has been moved into the inner flatMap so that it is not shared between multiple Observers.