I have a simple authentication system implemented using Volley. It happens like this: Get a token from the server when you log in β in an hour, this token expires β when it expires, we will find it when the API call fails, so we must (when restarting) β extract a new token when this call ends failure and then β repeat the initial call.
I implemented this, and the token returns successfully, but since I think I'm doing something wrong with the Volley RequestQueue, the original request uses all of its retries before it can use the new and valid token. See the following code:
public class GeneralAPICall extends Request<JSONObject> { public static String LOG_TAG = GeneralAPICall.class.getSimpleName(); SessionManager sessionManager;
The installed retry policy is defined as the default, but I implement my own retry method as such:
@Override public void retry(VolleyError error) throws VolleyError { Log.v(LOG_TAG, "Initiating a retry"); mCurrentRetryCount++; //increment our retry count mCurrentTimeoutMs += (mCurrentTimeoutMs * mBackoffMultiplier); if (error instanceof AuthFailureError) { //we got a 401, and need a new token Log.v(LOG_TAG, "AuthFailureError found!"); VolleyUser.refreshTokenTask(context, this); //**GET A NEW TOKEN** } if (!hasAttemptRemaining()) { Log.v(LOG_TAG, "No attempt remaining, ERROR"); throw error; } }
Update Token Task Defines RefreshAPICall
public static void refreshTokenTask(Context context, IRefreshTokenReturn listener) { Log.v(LOG_TAG, "refresh token task called"); final IRefreshTokenReturn callBack = listener; RefreshAPICall request = new RefreshAPICall(Request.Method.GET, Constants.APIConstants.URL.GET_TOKEN_URL, context, new Response.Listener<JSONObject>() { @Override public void onResponse(JSONObject response) { try { String token = response.getString(Constants.APIConstants.Returns.RETURN_TOKEN); Log.v(LOG_TAG, "Token from return is: " + token); callBack.onTokenRefreshComplete(token); } catch (JSONException e) { callBack.onTokenRefreshComplete(null);
The definition of our RefreshAPICall:
public RefreshAPICall(int method, String url, Context context, Response.Listener<JSONObject> responseListener, Response.ErrorListener errorListener) { super(method, url, errorListener); sessionManager = new SessionManager(context); //instantiate HashMap<String, String> credentials = sessionManager.getRefreshUserDetails(); //get the user credentials for authentication this.listener = responseListener; //encode the user username and token String loginEncoded = new String(Base64.encode((credentials.get(Constants.SessionManagerConstants.KEY_USERNAME) + Constants.APIConstants.Characters.CHAR_COLON + credentials.get(Constants.SessionManagerConstants.KEY_PASSWORD)).getBytes(), Base64.NO_WRAP)); this.headers.put(Constants.APIConstants.BasicAuth.AUTHORIZATION, Constants.APIConstants.BasicAuth.BASIC + loginEncoded); //set the encoded information as the header setTag(Constants.VolleyConstants.RETRY_TAG); //mark the retry calls with a tag so we can delete any others once we get a new token setPriority(Priority.IMMEDIATE); //set priority as immediate because this needs to be done before anything else //debug lines Log.v(LOG_TAG, "RefreshAPICall made with " + credentials.get(Constants.SessionManagerConstants.KEY_USERNAME) + " " + credentials.get(Constants.SessionManagerConstants.KEY_PASSWORD)); Log.v(LOG_TAG, "Priority set on refresh call is " + getPriority()); Log.v(LOG_TAG, "Tag for Call is " + getTag()); }
I set the priority of this request to high so that it fires to one that was unsuccessful, so as soon as we get the token, the original call can start with a valid token.
Finally, when I answer, I delete any other tasks with the retry tag (in the case of failure of several API calls and the execution of multiple retry calls, we do not want to rewrite the new token several times)
@Override public void onTokenRefreshComplete(String token) { VolleySingleton.getInstance(context).getRequestQueue().cancelAll(Constants.VolleyConstants.RETRY_TAG); Log.v(LOG_TAG, "Cancelled all retry calls"); SessionManager sessionManager = new SessionManager(context); sessionManager.setStoredToken(token); Log.v(LOG_TAG, "Logged new token"); }
Unfortunately, LogCat shows me that all attempts are made before we use the token. The token returns successfully, but it is obvious that IMMEDIATE priority does not affect the order in which the queue sends calls.
Any help on how to ensure that my RefreshAPICall is running before other tasks are greatly appreciated. I am wondering if Volley considers RefreshAPICall as a sub-task of the original unsuccessful task and therefore tries to call this original task for its number of repeaters until they exit, and then turns RefreshAPICall off.
LogCat (not sure how to make this look beautiful):
05-05 16:12:07.145: E/Volley(1972): [137] BasicNetwork.performRequest: Unexpected response code **401 for https://url.me/api/get_friends** 05-05 16:12:07.145: V/TokenRetryPolicy(1972): Initiating a retry 05-05 16:12:07.145: V/TokenRetryPolicy(1972): AuthFailureError found! 05-05 16:12:07.146: V/VolleyUser(1972): refresh token task called 05-05 16:12:07.146: V/RefreshAPICall(1972): RefreshAPICall made with username user_password 05-05 16:12:07.147: V/RefreshAPICall(1972): Priority set on refresh call is HIGH 05-05 16:12:07.147: V/RefreshAPICall(1972): Tag for Call is retry 05-05 16:12:07.265: E/Volley(1972): [137] BasicNetwork.performRequest: Unexpected response code **401 for https://url.me/api/get_friends** 05-05 16:12:07.265: V/TokenRetryPolicy(1972): Initiating a retry 05-05 16:12:07.265: V/TokenRetryPolicy(1972): AuthFailureError found! 05-05 16:12:07.265: V/VolleyUser(1972): refresh token task called 05-05 16:12:07.265: V/RefreshAPICall(1972): RefreshAPICall made with user user_password 05-05 16:12:07.265: V/RefreshAPICall(1972): Priority set on refresh call is HIGH 05-05 16:12:07.265: V/RefreshAPICall(1972): Tag for Call is retry 05-05 16:12:07.265: V/TokenRetryPolicy(1972): No attempt remaining, ERROR 05-05 16:12:08.219: I/Choreographer(1972): Skipped 324 frames! The application may be doing too much work on its main thread. 05-05 16:12:08.230: V/RefreshAPICall(1972): Response from server on refresh is: {"status":"success","token":"d5792e18c0e1acb3ad507dbae854eb2cdc5962a2c1b610a6b77e3bc3033c7f64"} 05-05 16:12:08.230: V/VolleyUser(1972): Token from return is: d5792e18c0e1acb3ad507dbae854eb2cdc5962a2c1b610a6b77e3bc3033c7f64 05-05 16:12:08.231: V/TokenRetryPolicy(1972): Cancelled all retry calls 05-05 16:12:08.257: V/SessionManager(1972): New Token In SharedPref is: d5792e18c0e1acb3ad507dbae854eb2cdc5962a2c1b610a6b77e3bc3033c7f64 05-05 16:12:08.257: V/TokenRetryPolicy(1972): Logged new token