Why Volley doesn't clear then update the cache

2020-06-29 05:58发布

问题:

Refering to Setting Up a RequestQueue, I have built an example of a singleton class with caching, in which:

private RequestQueue getRequestQueue() {
    if (mRequestQueue == null) {            
        mRequestQueue = Volley.newRequestQueue(mContext.getApplicationContext(), 10 * 1024 * 1024);
    }
    return mRequestQueue;
}

In MainActivity.java:

JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(0, mUrl, new Response.Listener<JSONObject>() {
        @Override
        public void onResponse(JSONObject response) {                
            try {
                mTextView.setText(response.toString(5));
            } catch (JSONException e) {
                mTextView.setText(e.toString());
            }
        }
    }, new Response.ErrorListener() {
        @Override
        public void onErrorResponse(VolleyError error) {                
        }
    }); 

My case:

Server data value is 1234:

  • Wifi connected, run Android app, get value: 1234
  • Wifi disconnected, run app, get value: 1234

Server data value is updated to 12345678:

  • Wifi connected, run app, get value: 12345678

  • Wifi disconnected, run app, get value: 1234

Why Volley doesn't clear then update the cache when getting new data (data different from cached data) from server? How to force that?

回答1:

I have found a fact (I don't know if it is an answer or not) that mRequestQueue = Volley.newRequestQueue(mContext.getApplicationContext(), 10 * 1024 * 1024); does not cache.

The JsonObjectRequest in my question in fact gets cache which is the result of another request which has the same Url. I will tell in details (sorry for my bad English, perhaps not clear):

In my Android app, firstly I create a JsonObjectRequest as the following:

JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(0, mUrl, new Response.Listener<JSONObject>() {
        @Override
        public void onResponse(JSONObject response) {
            try {
                mTextView.setText(response.toString(5));
            } catch (JSONException e) {
                mTextView.setText(e.toString());
            }
        }
    }, new Response.ErrorListener() {
        @Override
        public void onErrorResponse(VolleyError error) {

        }
    }) {
        @Override
        protected Response<JSONObject> parseNetworkResponse(NetworkResponse response) {
            try {
                Cache.Entry cacheEntry = HttpHeaderParser.parseCacheHeaders(response);
                if (cacheEntry == null) {
                    cacheEntry = new Cache.Entry();
                }
                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
                long now = System.currentTimeMillis();
                final long softExpire = now + cacheHitButRefreshed;
                final long ttl = now + cacheExpired;
                cacheEntry.data = response.data;
                cacheEntry.softTtl = softExpire;
                cacheEntry.ttl = ttl;
                String headerValue;
                headerValue = response.headers.get("Date");
                if (headerValue != null) {
                    cacheEntry.serverDate = HttpHeaderParser.parseDateAsEpoch(headerValue);
                }
                headerValue = response.headers.get("Last-Modified");
                if (headerValue != null) {
                    cacheEntry.lastModified = HttpHeaderParser.parseDateAsEpoch(headerValue);
                }
                cacheEntry.responseHeaders = response.headers;
                final String jsonString = new String(response.data,
                        HttpHeaderParser.parseCharset(response.headers));
                return Response.success(new JSONObject(jsonString), cacheEntry);
            } catch (UnsupportedEncodingException e) {
                return Response.error(new ParseError(e));
            } catch (JSONException e) {
                return Response.error(new ParseError(e));
            }
        }

        @Override
        protected void deliverResponse(JSONObject response) {
            super.deliverResponse(response);
        }

        @Override
        public void deliverError(VolleyError error) {
            super.deliverError(error);
        }

        @Override
        protected VolleyError parseNetworkError(VolleyError volleyError) {
            return super.parseNetworkError(volleyError);
        }
    };

    MySingleton.getInstance(this).addToRequestQueue(jsonObjectRequest);

This request runs and caches well, refreshs new data (after 2-3 minutes I guest)

Then, I comment this request (named request A) and create another request (the one in my question, named request B), re-run (Shift-F10) my Android app, and get the issue as in my question. I think B gets the cached data that created before by A.

  • If wifi disconnected: At first, only onResponse in B called, but 2-3 minutes later both onResponse and onErrorResponse called (onResponse before onErrorResponse). After the first onErrorResponse called, when app runs again, onErrorResponse called right after onResponse (not after 2-3 minutes anymore). The textView displays cached data (created by A before).

  • If wifi connected: only onResponse in B called, however, at first textView displays cached data (created by A before), very soon later, it displayed data from server. I think onResponse called twice.

Then, if I uninstall the Android app from the phone, run app (Shift-F10) again, B does not get cached data anymore.