3
votes

I am using Retrofit 2 along with RxJava 2 adapters.

Whenever the server returns 401 Unauthorized, I refresh the token and retry the request:

apiCall.retryWhen(es -> es
    .flatMap(e -> {
        if (isUnauthorizedException(e)) {
            return refreshToken();
        }

        return Flowable.<User>error(e);
    }));

Where refreshToken is a simple retrofit call:

public Flowable<User> refreshToken() {
    return retrofitService.login(userCredentials);
}

Now, I would like to limit the number of times such refresh is possible. However, simply adding take(1) does not work, because then retryWhen receives onCompleted immediately after onNext and cancels the request before retrying it.

Naturally I could do take(2) to achieve the desired effect but it seems like a hack.

What is the most elegant way to achieve it using Rx operators? Also is there an operator with "assertion" logic (to get rid of if in the flat map)?

Also I am aware I can achieve the same thing using OkHttp interceptors, but I am interested in Retrofit-RxJava solution.

1
Did you try retry? - public Flowable<User> refreshToken() { return retrofitService.login(userCredentials).retry(3); } - would retry max 3 times before emitting an error. - Fred

1 Answers

2
votes

There is a nice example from documentation
this retries 3 times, each time incrementing the number of seconds it waits.

ObservableSource.create((Observer<? super String> s) -> {

  System.out.println("subscribing");
  s.onError(new RuntimeException("always fails"));

}).retryWhen(attempts -> {

  return attempts.zipWith(ObservableSource.range(1, 3), 
    (n, i) -> i).flatMap(i -> {

    System.out.println("delay retry by " + i + " second(s)");
    return ObservableSource.timer(i, TimeUnit.SECONDS);

  });
}).blockingForEach(System.out::println);

Output is:

  • subscribing
  • delay retry by 1 second(s)
  • subscribing delay retry by 2 second(s)
  • subscribing delay retry by 3 second(s)
  • subscribing

you could modify this example according to your requirements.