Monday, March 23, 2015

Customize volley default retry Policy & Cache timeout

Volley Default request retry policy:

/** The default socket timeout in milliseconds */
public static final int DEFAULT_TIMEOUT_MS = 2500;

/** The default number of retries */
public static final int DEFAULT_MAX_RETRIES = 1;

/** The default backoff multiplier */
public static final float DEFAULT_BACKOFF_MULT = 1f;


Volley does retry for you if you have specified the policy. 
Client app can change retry values for each request using below API.

setRetryPolicy(new DefaultRetryPolicy (TIMEOUT_MS, MAX_RETRIES,  BACKOFF_MULT  ));


Example Timeout - 3000 secs, Num of retry - 2, Back Off Multiplier - 2
Attempt 1: 
- time = time + (time * Back Off Multiplier );
- time = 3000 + 6000 = 9000
- Socket Timeout = time;
- Request dispatched with Socket Timeout of 9 Secs
Attempt 2: 
- time = time + (time * Back Off Multiplier );
- time = 9000 + 18000 = 27000
- Socket Timeout = time;
- Request dispatched with Socket Timeout of 27 Secs


Steps to change Volley default Cache Hit & Cache time-out values:

1. Create new Volley Request class.
2. Set custom cacheHit & cacheTimeout values in parseNetworkResponse callback method.

Please refer below example.

GsonRequest.Java

import java.io.UnsupportedEncodingException;
import java.util.Map;

import android.util.Log;

import com.android.volley.AuthFailureError;
import com.android.volley.DefaultRetryPolicy;
import com.android.volley.NetworkResponse;
import com.android.volley.ParseError;
import com.android.volley.Request;
import com.android.volley.Response;
import com.android.volley.Response.ErrorListener;
import com.android.volley.Response.Listener;
import com.android.volley.toolbox.HttpHeaderParser;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonSyntaxException;
import com.samsung.android.guardian.utils.Config;

public class GsonRequest<T> extends Request<T> {

 private static final String TAG = GsonRequest.class.getSimpleName();
 private final Gson gson;
 private final Class<T> clazz;
 private final Map<String, String> headers;
 private final Listener<T> listener;
 
 private int cacheHit;
 private int cacheExpiry;

 public GsonRequest(int method, String url, Class<T> clazz,
   Map<String, String> headers, Listener<T> listener,
   ErrorListener errorListener) {
  super(method, url, errorListener);
  
  //Using default volley retry policy
  //If retry policy has to be changed use setRetryPolicy methods from each request objects
  if(Config.VOOLEY_USE_DEFAULT_RETRY_POLICY){
  setRetryPolicy(new DefaultRetryPolicy(
    DefaultRetryPolicy.DEFAULT_TIMEOUT_MS, DefaultRetryPolicy.DEFAULT_MAX_RETRIES,
    DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
  }
  GsonBuilder gsonBuilder = new GsonBuilder();

  this.gson = gsonBuilder.create();
  this.clazz = clazz;
  this.headers = headers;
  this.listener = listener;
  
  this.cacheHit = Config.DEFAULT_CACHE_HIT;
  this.cacheExpiry = Config.DEFAULT_CACHE_EXPIRY;
 }
 

 /**
 * @param method - GET/POST/PUT/DELETE
 * @param url - server URL
 * @param clazz - class represents server response
 * @param headers
 * @param listener - listener for success case
 * @param errorListener - listener for failure case
 * @param cacheHit - time milliseconds, after this time cache will be hit, but also refreshed on background.
 *     0 for CacheHit = CacheExpiry case
 * @param cacheExpiry - time milliseconds, after this time cache entry expires completely
 */
 public GsonRequest(int method, String url, Class<T> clazz,
   Map<String, String> headers, Listener<T> listener,
   ErrorListener errorListener, int cacheHit, int cacheExpiry) {
  this(method,url, clazz, headers, listener, errorListener);
  this.cacheHit = cacheHit;
  this.cacheExpiry = cacheExpiry;
 }

 @Override
 public Map<String, String> getHeaders() throws AuthFailureError {
  return headers != null ? headers : super.getHeaders();
 }

 @Override
 protected void deliverResponse(T response) {
  listener.onResponse(response);
 }

 @Override
 protected Response<T> parseNetworkResponse(NetworkResponse response) {
  try {
   String json = new String(response.data,
     HttpHeaderParser.parseCharset(response.headers));
   Log.i(TAG,"parseNetworkResponse data "+json);

   if(Config.VOOLEY_USE_DEFAULT_CACHE_TIMEOUT){
     //Volley default Cache
    return Response.success(gson.fromJson(json, clazz),
      HttpHeaderParser.parseCacheHeaders(response));
   }else{
    // Apply custom cache
    return Response.success(gson.fromJson(json, clazz),
      HttpHeaderParser.parseIgnoreCacheHeaders(response, cacheHit, cacheExpiry));
   }
  } catch (UnsupportedEncodingException e) {
   return Response.error(new ParseError(e));
  } catch (JsonSyntaxException e) {
   return Response.error(new ParseError(e));
  }
 }
}


HttpHeaderParser.java - only new method added

    public static Cache.Entry parseIgnoreCacheHeaders(NetworkResponse response, int cacheHit, int cacheExpiry) {
        long now = System.currentTimeMillis();

        Map<String, String> headers = response.headers;

        long serverDate = 0;

        String serverEtag = null;
        String headerValue;

        headerValue = headers.get("Date");
        if (headerValue != null) {
            serverDate = parseDateAsEpoch(headerValue);
        }

        serverEtag = headers.get("ETag");
        
        final long ttl = now + cacheExpiry;
        
        final long softExpire;
        if(cacheHit > 0){
         softExpire = now + cacheHit;
        }else{
         softExpire = ttl;
        }
        
        Log.i(TAG, "parseIgnoreCacheHeaders softExpire: "+softExpire+", ttl:"+ttl);

        Cache.Entry entry = new Cache.Entry();
        entry.data = response.data;
        entry.etag = serverEtag;
        entry.softTtl = softExpire;
        entry.ttl = ttl;
        entry.serverDate = serverDate;
        entry.responseHeaders = headers;

        return entry;
    }

1 comment: