Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cache.Entry not getting json data

I'm using volly to retrieve data and its work perfectly, except that my json array is not storing in the cache.

Here is my code:

private void getCacheValue() {
    Cache cache = AppController.getInstance().getRequestQueue().getCache();
    Cache.Entry entry = cache.get(Endpoints.product_url);


    if(entry != null){
        Log.w("Logdata:", ""+ entry.toString());
        try {
            String data = new String(entry.data, "UTF-8");
            JSONArray jsonArray = new JSONArray(data);

            // handle data, like converting it to xml, json, bitmap etc.,
            Log.v("Hello", data);
            listProduct.clear();
            for (int i = 0; i < jsonArray.length(); i++) {
                try {
                    JSONObject object = jsonArray.getJSONObject(i);
                    ItemCategories image = new ItemCategories();
                    image.setCategoryItem(object.getString(key_title));
                    image.setUrlThumb(object.getString(key_image));

                    listProduct.add(image);
                    } catch (JSONException e) {
                        Log.e(TAG, "Json parsing error: " + e.getMessage());
                    }
                }

                adapterProductList.notifyDataSetChanged();
                progressBarMain.setVisibility(View.GONE);
                internetError.setVisibility(View.GONE);
                recycleProductList.setVisibility(View.VISIBLE);

            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            } catch (JSONException e) {
                e.printStackTrace();
                Log.v("JSON EXCEPTION", "DECLINE");
            }
    } else {
        if(isNetworkAvailable()) {
            fetchImages();
        } else {
            progressBarMain.setVisibility(View.GONE);
            internetError.setVisibility(View.VISIBLE);
        }
    }
}

private void fetchImages() {
    JsonObjectRequest jsObjRequest = 
        new JsonObjectRequest(Request.Method.GET, Endpoints.product_url(String) null, 
            new Response.Listener<JSONObject>() {    
                @Override
                public void onResponse(JSONObject response) {
                    listProduct.clear();
                    try {
                        JSONArray routes = response.getJSONArray(key_product);
                        for (int i = 0; i < routes.length(); i++) {
                            JSONObject object = routes.getJSONObject(i);
                            ItemCategories categories = new ItemCategories();
                            categories.setCategoryItem(object.getString(key_title));
                            categories.setUrlThumb(object.getString(key_image)); 

                            listProduct.add(categories);
                        }
                        adapterProductList.notifyDataSetChanged();
                        progressBarMain.setVisibility(View.GONE);
                        internetError.setVisibility(View.GONE);
                        recycleProductList.setVisibility(View.VISIBLE);
                        } catch (JSONException e) {
                            e.printStackTrace();
                            Toast.makeText(getApplicationContext(),
                                           "Error: " + e.getMessage(),
                                            Toast.LENGTH_LONG).show();
                         }
                     }
                  }, 
                  new Response.ErrorListener() {
                      @Override
                      public void onErrorResponse(VolleyError error) {
                          VolleyLog.d(TAG, "Error: " + error.getMessage());
                      }
                  });
    // Adding request to request queue
    AppController.getInstance().addToRequestQueue(jsObjRequest);
}

I have already declared Appcontroller in the manifest, but I don't know why caching is not working.

Here is my json_data

fetchImage() is working because there is data in recyclerview. However, when I try to retrieve the data offline it does show any because my cache can't store any data.

like image 651
Sagar Chavada Avatar asked Aug 19 '16 07:08

Sagar Chavada


1 Answers

By default, Volley only caches data if Response Header permits. Volley caches response on the basis of following response headers:
1. Cache-Control
2. Expires
3. maxAge

See below function for details :

public static Cache.Entry parseCacheHeaders(NetworkResponse response) {
    long now = System.currentTimeMillis();

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

    long serverDate = 0;
    long lastModified = 0;
    long serverExpires = 0;
    long softExpire = 0;
    long finalExpire = 0;
    long maxAge = 0;
    long staleWhileRevalidate = 0;
    boolean hasCacheControl = false;
    boolean mustRevalidate = false;

    String serverEtag = null;
    String headerValue;

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

    headerValue = headers.get("Cache-Control");
    if (headerValue != null) {
        hasCacheControl = true;
        String[] tokens = headerValue.split(",");
        for (int i = 0; i < tokens.length; i++) {
            String token = tokens[i].trim();
            if (token.equals("no-cache") || token.equals("no-store")) {
                return null;
            } else if (token.startsWith("max-age=")) {
                try {
                    maxAge = Long.parseLong(token.substring(8));
                } catch (Exception e) {
                }
            } else if (token.startsWith("stale-while-revalidate=")) {
                try {
                    staleWhileRevalidate = Long.parseLong(token.substring(23));
                } catch (Exception e) {
                }
            } else if (token.equals("must-revalidate") || token.equals("proxy-revalidate")) {
                mustRevalidate = true;
            }
        }
    }

    headerValue = headers.get("Expires");
    if (headerValue != null) {
        serverExpires = parseDateAsEpoch(headerValue);
    }

    headerValue = headers.get("Last-Modified");
    if (headerValue != null) {
        lastModified = parseDateAsEpoch(headerValue);
    }

    serverEtag = headers.get("ETag");

    // Cache-Control takes precedence over an Expires header, even if both exist and Expires
    // is more restrictive.
    if (hasCacheControl) {
        softExpire = now + maxAge * 1000;
        finalExpire = mustRevalidate
                ? softExpire
                : softExpire + staleWhileRevalidate * 1000;
    } else if (serverDate > 0 && serverExpires >= serverDate) {
        // Default semantic for Expire header in HTTP specification is softExpire.
        softExpire = now + (serverExpires - serverDate);
        finalExpire = softExpire;
    }

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

    return entry;
}

You can change default cache policy by overriding Request object. You can override JsonObjectRequest like :

public class CustomJsonObjectRequest extends JsonObjectRequest {
    public CustomJsonObjectRequest(int method, String url, JSONObject jsonRequest, Response.Listener<JSONObject> listener, Response.ErrorListener errorListener) {
        super(method, url, jsonRequest, listener, errorListener);
    }

    public CustomJsonObjectRequest(String url, JSONObject jsonRequest, Response.Listener<JSONObject> listener, Response.ErrorListener errorListener) {
        super(url, jsonRequest, listener, errorListener);
    }

    @Override
    protected Response<JSONObject> parseNetworkResponse(NetworkResponse response) {
        try {
            String jsonString = new String(response.data,
                    HttpHeaderParser.parseCharset(response.headers, PROTOCOL_CHARSET));
            return Response.success(new JSONObject(jsonString),
                    parseIgnoreCacheHeaders(response));
        } catch (UnsupportedEncodingException e) {
            return Response.error(new ParseError(e));
        } catch (JSONException je) {
            return Response.error(new ParseError(je));
        }
    }

    public static Cache.Entry parseIgnoreCacheHeaders(NetworkResponse response) {
        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 = HttpHeaderParser.parseDateAsEpoch(headerValue);
        }

        serverEtag = headers.get("ETag");

        final long cacheHitButRefreshed = 3 * 60 * 1000; // in 3 minutes cache will be hit, but also refreshed on background
        final long cacheExpired = 24 * 60 * 60 * 1000; // in 24 hours this cache entry expires completely
        final long softExpire = now + cacheHitButRefreshed;
        final long ttl = now + cacheExpired;

        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;
    }
}

Update your fetchImage function as:

private void fetchImages() {

    CustomJsonObjectRequest jsObjRequest = new CustomJsonObjectRequest()
            (Request.Method.GET, Endpoints.product_url, (String) null, new Response.Listener<JSONObject>() {

                @Override
                public void onResponse(JSONObject response) {
                    listProduct.clear();
                    try {
                        JSONArray routes = response.getJSONArray(key_product);
                        for (int i = 0; i < routes.length(); i++) {
                            JSONObject object = routes.getJSONObject(i);
                            ItemCategories categories = new ItemCategories();

                            categories.setCategoryItem(object.getString(key_title));
                            categories.setUrlThumb(object.getString(key_image));

                            listProduct.add(categories);
                        }
                        adapterProductList.notifyDataSetChanged();
                        progressBarMain.setVisibility(View.GONE);
                        internetError.setVisibility(View.GONE);
                        recycleProductList.setVisibility(View.VISIBLE);
                    } catch (JSONException e) {
                        e.printStackTrace();
                        Toast.makeText(getApplicationContext(),
                                "Error: " + e.getMessage(),
                                Toast.LENGTH_LONG).show();
                    }
                }
            }, new Response.ErrorListener() {
                @Override
                public void onErrorResponse(VolleyError error) {
                    VolleyLog.d(TAG, "Error: " + error.getMessage());

                }
            });
    // Adding request to request queue
    AppController.getInstance().addToRequestQueue(jsObjRequest);
}

For reference, check Android Volley + JSONObjectRequest Caching

like image 173
abhishesh Avatar answered Oct 04 '22 20:10

abhishesh