My current Android Application is employing Retrofit(2.4.0) and RxJava(2.1.16) to execute my Web Service calls.
Im using Google SignIn for my User Authentication.
I want my Retrofit calls to detect HTTP 401 (UNAUTHORIZED) and attempt to Silently Login with Google Signin then retry the Retrofit call.
My retrofit calls resemble this
@Headers(HEADER_ACCEPT_JSON)
@GET("resources")
Observable<Response<String>> getResources(@Header(HEADER_AUTHORIZATION) @NonNull final String authenticationToken, @QueryMap(encoded = true) @NonNull Map<String, Object> queryMap);
API_SERVICE.getResources(Login.getAuthorizationToken(), id)
.subscribeOn(Schedulers.io())
.subscribe(Network::manageResource, Network::handle));
From googling I can see that retry/retryWhen will only be triggered when an error occurs in my RxJava chain, however HTTP 401 errors are not going to raise this condition.
As a newbie to RxJava how can I detect my HTTP 401 code and..
a). Execute Google SignIn Silent login
b). Silent login completes OK, retry my API call?
UPDATE
Ive got closer with the following code
@Headers(HEADER_ACCEPT_JSON)
@GET("resources")
Single<Response<String>> getResources(@Header(HEADER_AUTHORIZATION) @NonNull final String authenticationToken, @QueryMap(encoded = true) @NonNull Map<String, Object> queryMap);
API_SERVICE.getResources(Login.getAuthorizationToken(), id)
.subscribeOn(Schedulers.io())
.flatMap(new Function<Response<Article>,
SingleSource<Response<Article>>>() {
@Override
public SingleSource<Response<Article>> apply(final Response<Article> response) {
Log.d(TAG, "apply() called with: response = [" + response + "]");
if (response.isSuccessful()) {
return Single.just(response);
} else {
return Single.error(new RuntimeException());
}
}
})
.retryWhen(errors -> errors.take(1).flatMap(new Function<Throwable, Publisher<?>>() {
@Override
public Publisher<?> apply(final Throwable throwable) {
Log.d(TAG, "apply() called with: throwable = [" + throwable + "]");
Login.loginSilently().subscribe();
return Flowable.just("DELAY").delay(10, TimeUnit.SECONDS);
}
}))
.subscribe(Network::manageResource, Network::handle));
I do not like the Flowable.just("DELAY").delay() call and also even though I am now catching the exception and silently login in OK I get this exception
09-10 16:39:29.878 7651-7718/research.android E/Network: handle:
java.util.NoSuchElementException
at io.reactivex.internal.operators.flowable.FlowableSingleSingle$SingleElementSubscriber.onComplete(FlowableSingleSingle.java:116)
at io.reactivex.subscribers.SerializedSubscriber.onComplete(SerializedSubscriber.java:168)
at io.reactivex.internal.operators.flowable.FlowableRepeatWhen$WhenReceiver.onComplete(FlowableRepeatWhen.java:119)
at io.reactivex.internal.operators.flowable.FlowableFlatMap$MergeSubscriber.drainLoop(FlowableFlatMap.java:426)
at io.reactivex.internal.operators.flowable.FlowableFlatMap$MergeSubscriber.drain(FlowableFlatMap.java:366)
at io.reactivex.internal.operators.flowable.FlowableFlatMap$InnerSubscriber.onComplete(FlowableFlatMap.java:673)
at io.reactivex.subscribers.SerializedSubscriber.onComplete(SerializedSubscriber.java:168)
at io.reactivex.internal.operators.flowable.FlowableDelay$DelaySubscriber$OnComplete.run(FlowableDelay.java:139)
at io.reactivex.internal.schedulers.ScheduledRunnable.run(ScheduledRunnable.java:66)
at io.reactivex.internal.schedulers.ScheduledRunnable.call(ScheduledRunnable.java:57)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:301)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
at java.lang.Thread.run(Thread.java:764)
09-10 16:39:29.878 7651-7678/research.android D/OkHttp: <-- HTTP FAILED: java.io.IOException: Canceled
How can I get the retrywhen to wait for the silentLogin to complete?
and
Whats causing the NoSuchElementException?