I am new to android programming and Retrofit , I am making a sample app where i have to make two parallel network calls using access token. The problem comes when access token is expired and return 401 status code , if I see 401 HTTP status code I have to make a call to refresh token with this access token , but problem with parallel calls is that it leads to race condition for refreshing the refresh token , is there any best practice of way to avoid such situation and how to intelligently refresh the token without any conflict.
-
1did you find the answer? I am also facing the same problem. – Mohit Rajput May 26 '20 at 05:47
-
@Ajay Beniwal did you find the answer? – Ranjeet Kumar yadav Oct 04 '21 at 07:11
-
Through synchronized code blocks you can resolve the problem. https://medium.com/bazaar-tech/the-dark-side-of-overusing-concurrency-7b9fa0dab9ab – Shahab Rauf Dec 04 '21 at 08:49
3 Answers
OkHttp will automatically ask the Authenticator for credentials when a response is 401 Not Authorised retrying last failed request with them.
public class TokenAuthenticator implements Authenticator {
@Override
public Request authenticate(Proxy proxy, Response response) throws IOException {
// Refresh your access_token using a synchronous api request
newAccessToken = service.refreshToken();
// Add new header to rejected request and retry it
return response.request().newBuilder()
.header(AUTHORIZATION, newAccessToken)
.build();
}
@Override
public Request authenticateProxy(Proxy proxy, Response response) throws IOException {
// Null indicates no attempt to authenticate.
return null;
}
Attach an Authenticator to an OkHttpClient the same way you do with Interceptors
OkHttpClient okHttpClient = new OkHttpClient();
okHttpClient.setAuthenticator(authAuthenticator);
Use this client when creating your Retrofit RestAdapter
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint(ENDPOINT)
.setClient(new OkClient(okHttpClient))
.build();
return restAdapter.create(API.class);
Check this: Fore more details visit this link

- 2,555
- 1
- 22
- 30
-
2This is not an answer to the question. The question asks for a problem with parallel requests! – Skynet Nov 02 '18 at 02:42
-
This is not the answer. The question is about parallel services call. I am also facing the same problem. – Mohit Rajput May 26 '20 at 05:46
Try to make a queue for the refresh token operations like:
class TokenProcessor {
private List<Listener> queue = new List<Listener>();
private final Object synch = new Object();
private State state = State.None;
private String token;
private long tokenExpirationDate;
public void getNewToken(Listener listener){
synchronized(synch) {
// check token expiration date
if (isTokenValid()){
listener.onSuccess(token);
return;
}
queue.add(listener);
if (state != State.Working) {
sendRefreshTokenRequest();
}
}
}
private void sendRefreshTokenRequest(){
// get token from your API using Retrofit
// on the response call onRefreshTokenLoaded() method with the token and expiration date
}
private void onRefreshTokenLoaded(String token, long expirationDate){
synchronized(synch){
this.token = token;
this.tokenExpirationDate = expirationDate;
for(Listener listener : queue){
try {
listener.onTokenRefreshed(token);
} catch (Throwable){}
}
queue.clear();
}
}
}
This is an example code, how it can be implemented.

- 385
- 3
- 14
To avoid race conditions, you could synchronize refresh token code using ReentrantLock. For instance, if request A and request B try to refresh token at the same time, since code is synchronized, refresh A gets to actually refresh the token. Once it completes, request B will run refreshToken() and there's should be some logic that tells request B that token has already been refreshed. An example could be storing timestamp of when the token refresh happens then check if token has been refreshed last 10 seconds.
val lock = ReentrantLock(true)
fun refreshToken(): Boolean {
lock.lock()
if (token has been refreshed in last 10 seconds): return true
api.refresh()
lock.unlock()
}
If you don't want to use last 10 seconds logic, here's a different approach. Whenever you refresh token, backend returns {accessToken, expiration-timestamp}. Now, request A saves this token and expiration in disk. Request B will just need to check to make sure token is not expired using the timestamp. If request B gets 401 and token has not expired, it means request A has refreshed the token. Sample code:
val lock = ReentrantLock(true)
fun refreshToken(): Boolean {
lock.lock()
if (token has not expired): return true
api.refresh()
lock.unlock()
}
Otherwise, you probably have to create a queue for refresh token operations as mentioned above.

- 431
- 5
- 9