Reputation: 5598
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)));
Upvotes: 7
Views: 1666
Reputation: 13012
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
.
Upvotes: 8
Reputation: 5598
While browsing the Internet for discovering a proper answer - i've found this cool gist describing how to refresh OAuth token with the help of OkHttp Interceptor
(similar to accepted answer, but more complete).
It has nothing to do with RxJava, but for me it's more acceptable because i don't have to wrap each Observable with retryWith
logic - everything is done on lower level (OkHttp
library).
Upvotes: 0
Reputation: 821
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.
Upvotes: 4