Retry Sometimes Using RetryWhen


Continuing from the last post.
Last week I was working through a way of retrying attempts. As a result, I thought of an easier way of retrying. Previously, I demoed a way to do this recursively. But I realized there is an easier way. Namely, using the RetryWhen operator.

As I was working through it, I had a bright idea. I realized that a 202 represented onNext, and onError. As a result, what if I converted a 202 into two objects?

A onNext object in addition to a onError event. As long as the onNext was emitted first, couldn’t I very easily then trigger a retry using the retryWhen operator?

Last week we had the following:

Http Code Notification Type(s)
200 onNext
202 onNext and onError
4XX onNext
5XX onNext

Seems like we should start with the rest call. It’s the same as before:

fun makeCall(maxAttempts: Long): Observable {
   return api.makeCall()
}

Next let’s pass through the success values (http code 200). But also create an error for when it hasn’t been processed (http code 202).

fun makeCall(maxAttempts: Long): Observable {
   return api.makeCall()
           .flatMap { response ->
               when {
                   response.isProcessed -> Observable.just(response)
                   else -> Observable.concat(Observable.just(response), Observable.error(RuntimeException()))
               }
           }
}

What does retryWhen do?

RetryWhen receives an Observable of errors. Each error will resubscribe to the observable upstream. Thus, causing a retry.

So, we can now use retryWhen. Since both 202 and 4xx and 5xx require retrying. That would look like the following:

fun makeCall(maxAttempts: Long): Observable {
   return api.makeCall()
           .flatMap { response ->
               when {
                   response.isProcessed -> Observable.just(response)
                   else -> Observable.concat(Observable.just(response), Observable.error(RuntimeException()))
               }
           }
           .retryWhen { errorStream ->
               errorStream
                       .flatMap {
                           Observable.timer(duration, TimeUnit.MILLISECONDS)
                       }
           }
}

Seems like we may at some point want to stop retrying. Therefore, we can use the take operator to stop.

Take Operator Marble Diagram used with the RetryWhen Operator

This leaves us with the following:

fun makeCall(maxAttempts: Long): Observable {
   return api.makeCall()
           .flatMap { response ->
               when {
                   response.isProcessed -> Observable.just(response)
                   else -> Observable.concat(Observable.just(response), Observable.error(RuntimeException()))
               }
           }
           .retryWhen { errorStream ->
               errorStream
                       .take(maxAttempts)
                       .flatMap {
                           Observable.timer(duration, TimeUnit.MILLISECONDS)
                       }
           }
}

In conclusion, that’s all there is to it. Due to flatMap and retryWhen we now have an easier way to succeed and retry at the same time.

Comments 1

  1. Pingback: Retry Sometimes in RxJava - Learn Android The Easy Way

Leave a Reply

Your email address will not be published. Required fields are marked *