How to Clear the Volley Cache Automatically

How to clear the volley cache automatically?

Google Volley provides 2 ways to clear an item from the Cache:

AppController.getInstance().getRequestQueue().getCache().remove(key);

and

AppController.getInstance().getRequestQueue().getCache().invalidate(key, fullExpire);

Remove means you are removing the actual cached data.

Invalidate means you are just marking the data as invalid. So volley will check with the server whether the data is still valid. The full expire determines whether to use the data before volley has validated it with the server.

To clear cache in each 30 minutes use below code:-

you can use volley's serverDate to get the date for when the response was originally received as

AppController.getInstance().getRequestQueue().getCache().get(url).serverDate

So in your code use getMinutesDifference function as

  public static long getMinutesDifference(long timeStart,long timeStop){
long diff = timeStop - timeStart;
long diffMinutes = diff / (60 * 1000);

return diffMinutes;
}

and Call this function in your code as

Calendar calendar = Calendar.getInstance();
long serverDate = AppController.getInstance().getRequestQueue().getCache().get(url).serverDate;
if(getMinutesDifference(serverDate, calendar.getTimeInMillis()) >=30){
AppController.getInstance().getRequestQueue().getCache().invalidate(url, true);
}

It will invalidate the cache,if previous url response >=30 minutes.

Clear stale entries from Cache in Volley-Android

I ended up extending Cache interface to provide me list of keys. I have posted the full solution here.

Volley NetworkImageView clear cached image

So to belatedly answer my own question. I ended up taking a different approach to ensure that I always get the latest copy of an avatar image and it turned out to be very simple.

Instead of trying to clear out an individual image from the cache I use the following method.

int time = (int) (System.currentTimeMillis());//gets the current time in milliseconds
String myUrl = "www.mySite.com?timestamp=" + String.ValueOf(time);
profileImg.setImageUrl("myUrl", mImageLoader);

What this does is to append an imaginary parameter to the url I call to get the image with a current timestamp. This guarantees that I am always making a call to a unique url and so therefore am always getting the most up-to-date version of that image.

The beauty of this approach is that it is not limited to Volley and can be used however you choose to make network calls.

Are Android Volley Requests Automatically Cached?

Yes, Volley caches every response, unless setShouldCache is set to false.

BUT, it does so according to the HTTP cache headers of the response. This means that if there are no cache headers, or they have expired, the JSON response (or any response for that matter) will NOT be cached.

setShouldCache is true by default so you don't have to set it to true manually. It's actually used to explicitly ask for the response not to be cached.

Also, the tutorial you're looking at is wrong. You do not need to manually interact with Volley's cache. Volley does that automatically.

Google Volley - when to use Cache.remove and Cache.invalidate

Remove means you are removing the actual cached data.

Invalidate means you are just marking the data as invalid. So volley will check with the server whether the data is still valid. The full expire determines whether to use the data before volley has validated it with the server.

More details in the source: https://android.googlesource.com/platform/frameworks/volley/+/master/src/main/java/com/android/volley/Cache.java

How to make a volley cache expire in exactly 1 minute

you can do:

 JsonObjectRequest jsObjRequest = new JsonObjectRequest
(Request.Method.GET, url, null, new Response.Listener<JSONObject>() {

@Override
public void onResponse(JSONObject response) {
mTxtDisplay.setText("Response: " + response.toString());
}
}, new Response.ErrorListener() {

@Override
public void onErrorResponse(VolleyError error) {
// TODO Auto-generated method stub

}
}){
@Override
protected Response<JSONObject> parseNetworkResponse(NetworkResponse response) {
Response<JSONObject> resp = super.parseNetworkResponse(response);
if(!resp.isSuccess()) {
return resp;
}
long now = System.currentTimeMillis();
Cache.Entry entry = resp.cacheEntry;
if(entry == null) {
entry = new Cache.Entry();
entry.data = response.data;
entry.responseHeaders = response.headers;
}
entry.ttl = now + 60 * 1000; //keeps cache for 1 min

return Response.success(resp.result, entry);
}
};

this will keep you cache for the request always and only for 1 min

Android set up Volley to use from cache

Please note that if the web service supports caching output, you don't have to use CacheRequest below, because Volley will automatically cache.


For your issue, I use some codes inside parseCacheHeaders (and refering to @oleksandr_yefremov's codes). The following code I have tested. Of course, can use for JsonArrayRequest also. Hope this help!

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

UPDATE:

If you want a base class, refer to the following codes:

public class CacheRequest extends Request<NetworkResponse> {
private final Response.Listener<NetworkResponse> mListener;
private final Response.ErrorListener mErrorListener;

public CacheRequest(int method, String url, Response.Listener<NetworkResponse> listener, Response.ErrorListener errorListener) {
super(method, url, errorListener);
this.mListener = listener;
this.mErrorListener = errorListener;
}

@Override
protected Response<NetworkResponse> parseNetworkResponse(NetworkResponse response) {
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;
return Response.success(response, cacheEntry);
}

@Override
protected void deliverResponse(NetworkResponse response) {
mListener.onResponse(response);
}

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

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

Then in MainActivity, you can call like this

CacheRequest cacheRequest = new CacheRequest(0, mUrl, new Response.Listener<NetworkResponse>() {
@Override
public void onResponse(NetworkResponse response) {
try {
final String jsonString = new String(response.data,
HttpHeaderParser.parseCharset(response.headers));
JSONObject jsonObject = new JSONObject(jsonString);
mTextView.setText(jsonObject.toString(5));
} catch (UnsupportedEncodingException | JSONException e) {
e.printStackTrace();
}
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
mTextView.setText(error.toString());
}
});

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

UPDATE WITH FULL SOURCE CODE:

MainActivity.java:

package com.example.cachevolley;

import android.content.Context;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.TextView;
import android.widget.Toast;

import com.android.volley.Cache;
import com.android.volley.NetworkResponse;
import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.HttpHeaderParser;
import com.android.volley.toolbox.Volley;

import org.json.JSONException;
import org.json.JSONObject;

import java.io.UnsupportedEncodingException;

public class MainActivity extends AppCompatActivity {

private final Context mContext = this;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

final TextView textView = (TextView) findViewById(R.id.textView);

RequestQueue queue = Volley.newRequestQueue(this);
String url = "http://192.168.0.100/apitest";

CacheRequest cacheRequest = new CacheRequest(0, url, new Response.Listener<NetworkResponse>() {
@Override
public void onResponse(NetworkResponse response) {
try {
final String jsonString = new String(response.data,
HttpHeaderParser.parseCharset(response.headers));
JSONObject jsonObject = new JSONObject(jsonString);
textView.setText(jsonObject.toString(5));
Toast.makeText(mContext, "onResponse:\n\n" + jsonObject.toString(), Toast.LENGTH_SHORT).show();
} catch (UnsupportedEncodingException | JSONException e) {
e.printStackTrace();
}
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Toast.makeText(mContext, "onErrorResponse:\n\n" + error.toString(), Toast.LENGTH_SHORT).show();
}
});

// Add the request to the RequestQueue.
queue.add(cacheRequest);
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();

//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}

return super.onOptionsItemSelected(item);
}

private class CacheRequest extends Request<NetworkResponse> {
private final Response.Listener<NetworkResponse> mListener;
private final Response.ErrorListener mErrorListener;

public CacheRequest(int method, String url, Response.Listener<NetworkResponse> listener, Response.ErrorListener errorListener) {
super(method, url, errorListener);
this.mListener = listener;
this.mErrorListener = errorListener;
}

@Override
protected Response<NetworkResponse> parseNetworkResponse(NetworkResponse response) {
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;
return Response.success(response, cacheEntry);
}

@Override
protected void deliverResponse(NetworkResponse response) {
mListener.onResponse(response);
}

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

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

Manifest file:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.cachevolley" >

<uses-permission android:name="android.permission.INTERNET" />

<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name=".MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>

</manifest>

Layout file:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity">

<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hello_world" />

</RelativeLayout>


Related Topics



Leave a reply



Submit