Reputation: 1794
I'm working with the authenticator
of OKHttp
that will retry to get new access token if we got 401
status error, but my app have to call many APIs in the same time, resulting in corrupted data, because existed refresh token will be removed when request - but the other API caller still depend on this token to use. So my question : is there anyway to put the request in queue (or at least cancel) all other api request when we got 401 error status code?
This is my authenticator:
public Request authenticate(Route route, Response response) throws IOException {
// Refresh your access_token using a synchronous api request
access_token = getNewAccessTokenHere();
// Add new header to rejected request and retry it
return response.request().newBuilder()
.header("Authorization", "Bearer " + access_token)
.build();
} else {
ToastUtil.toast("login again");
return null;
}
}
My goal is let other api waiting for the response of first request and use the new access_token.
Upvotes: 5
Views: 5847
Reputation: 61
I know this question is quite old, but I found myself having the same problem lately and I came by a solution which has worked for me and I believe it could help someone else in the same situation.
The solution to attach a Dispatcher to the OkHttp client and limit the amount of max requests does not seem to work on retrofit out of the box, as stated by Jake Wharthon .
So my solution was to synchronize the authenticate method on my custom Authenticator, making concurrent calls, to my singleton Retrofit instance, wait until the authenticate routine is finished for each thread. This way, the first call with an unauthorized response can refresh the token and inform to the next calls, which also got an unauthorized response, that a new access token is already available.
public class MyAuthenticator implements Authenticator {
private boolean isRefreshed = false;
// Call when a new request is being made. Concurrent request should call this method to enable the refresh routine
public void newRequest(){
isRefreshed = false;
}
@Nullable
@Override
public synchronized Request authenticate(@NonNull Route route, @NonNull Response response) throws IOException { // Synchronize the method to avoid refreshing thread overlapping
if (responseCount(response) > 3) {
return null;
}
if (!isRefreshed){
// Refresh your access_token using a synchronous api request
String accessToken = getNewAccessTokenHere();
// Saves the new access token
saveNewAccessToken(accessToken);
isRefreshed = true;
// Add new header to rejected request and retry it
return response.request().newBuilder()
.removeHeader("Authorization") // removes the old header, avoiding duplications
.addHeader("Authorization", "Bearer " + accessToken)
.build();
}
else{ // Access token already refreshed so retry with the new one
// Get the saved access token
String accessToken = getAccessToken();
return response.request()
.newBuilder()
.removeHeader("Authorization")
.addHeader("Authorization", accessToken)
.build();
}
}
private int responseCount(Response response) {
int result = 1;
while ((response = response.priorResponse()) != null) {
result++;
}
return result;
}
}
Upvotes: 4
Reputation: 40593
You can use the Dispatcher
to access all in-flight calls and cancel them.
https://square.github.io/okhttp/3.x/okhttp/okhttp3/Dispatcher.html
Upvotes: 2