I have an app which requires session (cookies) to process web calls. Im using Retrofit+RxJava
. However, session could expire (Retrofit error with 401 Unauthorized status) and i want to reauthenticate (to get fresh cookies) and retry previous call in this case. How would i do it with RxJava
?
My example:
getServerApi().getDialogs(offset, getCookies())
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.retryWhen(observable -> {...}) // Need some logic
.subscribe(dialogsEnvelope -> getView().setDialogs(dialogsEnvelope),
throwable -> getView().setError(processFail(throwable)));
Configurable Retry Logic is an eCommerce platform feature that recovers failed transactions due to soft declines.
While an Interceptor
may be a better solution for this particular problem, the question specifically asked for a solution using retryWhen
, so here is is one way to do it:
retryWhen(new Func1<Observable<Throwable>, Observable<?>>(){
@Override
public void Observable<?> call(Observable<Throwable>> attempts) {
return attempts.flatMap(new Func1<Throwable, Observable<?>>() {
@Override
public Observable<?> call(Throwable throwable) {
if (throwable instanceof RetrofitError) {
RetrofitError retrofitError = (RetrofitError) throwable;
if (retrofitError.getKind() == RetrofitError.Kind.HTTP && retrofitError.getResponse().getStatus() == 401) {
// this is the error we care about - to trigger a retry we need to emit anything other than onError or onCompleted
return Observable.just(new Object());
} else {
// some other kind of error: just pass it along and don't retry
return Observable.error(throwable);
}
} else {
// some other kind of error: just pass it along and don't retry
return Observable.error(throwable);
}
}
});
}
})
However, your getCookies
would not be called again in the case of a simple retry
. That would just resubscribe to the same Observable
but getCookies
was called before the creation of that Observable
. So I think you would have to wrap the creation of the source Observable
in a defer
.
Use OkHttp's extremely powerful Interceptor
.
public class RecoverInterceptor implements Interceptor {
String getAuth() {
// check if we have auth, if not, authorize
return "Bearer ...";
}
void clearAuth() {
// clear everything
}
@Override public Response intercept(Chain chain) throws IOException {
final Request request = chain.request();
if (request.urlString().startsWith("MY ENDPOINT")) {
final Request signed = request.newBuilder()
.header("Authorization", getAuth())
.build();
final Response response = chain.proceed(signed);
if (response.code() == 401) {
clearAuth();
return intercept(chain);
} else {
return response;
}
} else {
return chain.proceed(request);
}
}
}
Remember to synchronize your auth process code, so that two concurrent requests do not invoke it at the same time.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With