I am using Google's Volley Library as my design for getting network data;
I have set up a RequestQueue
requestQueue = new RequestQueue(new DiskBasedCache(new File(context.getCacheDir(),
DEFAULT_CACHE_DIR)), new BasicNetwork(new
HttpClientStack(AndroidHttpClient.newInstance(userAgent))));
I have also subclassed Request, and have data coming back from the network just fine. My issue is with caching: in parseNetworkResponse() which is overridden in my subclass of Request, when I call
return Response.success(list, HttpHeaderParser.parseCacheHeaders(response));
HttpHeaderParser.parseCacheHeaders(response) returns null since the server is set up for "no caching" in its response header... Regardless I still would like to cache this data for a variable set number of hours (24 hours probably), How can I do this by creating a volley Cache.Entry... It is my understanding that the URL is used as the cache key value (and I would like it to be the URL).
To sum up, since HttpHeaderParser.parseCacheHeaders(response) returns null, I would like to create a new Cache.Entry that is set up for expiring after 24 hours, and the cache key being the URL of the request.
Any thoughts?
Thanks!
I've had the same issue and ended up with this solution:
#Override
protected Response<String> parseNetworkResponse(NetworkResponse response) {
// Create a FakeCache that invalidate the data after 24 hour
Cache.Entry mFakeCache = HttpHeaderParser.parseCacheHeaders(response);
mFakeCache.etag = null;
mFakeCache.softTtl = System.currentTimeMillis() + 86400 * 1000;
mFakeCache.ttl = mFakeCache.softTtl;
return Response.success(response.data, mFakeCache);
}
Related
In one of my application, I am sending request to server using volley provided by Google.
Problem : Timeout and error object is null on onErrorResponse(VolleyError error)
What i have tried so far :
1) First I got null error object so solved it by using below code :
#Override
protected void deliverResponse(String response) {
super.deliverResponse(response);
}
#Override
public void deliverError(VolleyError error) {
super.deliverError(error);
DebugLog.e("deliverResponse", "getNetworkTimeMs : " + error.getNetworkTimeMs());
}
So far I have got that there is timeout happening when I got error object null.
2) Now Application is for Android and iOS and web but timeout happens only for Android.
Volley log for requests :
BasicNetwork.logSlowRequests: HTTP response for request
Edited Note :
Web services develoed at server end is same for all three instances (Android , Web and iOS).
Timeout happens when too many users makes requests to the server.
I have set time out to 2 minutes though volley throws timeout in 30 seconds only sometimes.
I have many answers to change server but as it is not possible so any other solution please.
I also like to add that if i can get more information about when timeout can be possible in volley ?
References I have been gone through :
Optimizing Volley
httpclient-often-times-out-using-wifi-is-going-fine-with-3g
long_xmlhttprequest_ajax_requests_timeout_on_android
Edited :
I have also set retry policy as below:
request.setRetryPolicy(new DefaultRetryPolicy(DefaultRetryPolicy.DEFAULT_TIMEOUT_MS * 48,
0, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
And also i do not want to retry if connection timeout.
How can i make efficient service call that can solve problem for timeout.
Any help will be appriciated.
Thanks.
As i have tried to get solution of this issue for about two months, I did not get any perfect solution. Though I analyze some facts as below :
You can upgrade your server's performance
I have tried making web-service request using HttpURLConnection but still getting same issue over there.
So I think this issue is not specific from volley, but you getting this issue then i would suggest to increase server performance with customizing below RetryPolicy:
int x=2;// retry count
request.setRetryPolicy(new DefaultRetryPolicy(DefaultRetryPolicy.DEFAULT_TIMEOUT_MS * 48,
x, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
Hope it will help.
Suggestions are always welcome :)
Please comment below if you found more proper solution.
Thanks.!
IMHO, you can refer to the following:
Inside BasicNetwork.java, you will find some information such as:
...
private static int SLOW_REQUEST_THRESHOLD_MS = 3000;
...
/**
* Logs requests that took over SLOW_REQUEST_THRESHOLD_MS to complete.
*/
private void logSlowRequests(long requestLifetime, Request<?> request,
byte[] responseContents, StatusLine statusLine) {
if (DEBUG || requestLifetime > SLOW_REQUEST_THRESHOLD_MS) {
VolleyLog.d("HTTP response for request=<%s> [lifetime=%d], [size=%s], " +
"[rc=%d], [retryCount=%s]", request, requestLifetime,
responseContents != null ? responseContents.length : "null",
statusLine.getStatusCode(), request.getRetryPolicy().getCurrentRetryCount());
}
}
...
// if the request is slow, log it.
long requestLifetime = SystemClock.elapsedRealtime() - requestStart;
logSlowRequests(requestLifetime, request, responseContents, statusLine);
...
So, if your project uses Google's volley as a module (not JAR file), you can update BasicNetwork.java, increasing SLOW_REQUEST_THRESHOLD_MS value, perhaps 10000 (ms) or more, for example.
Another option, according to #neuron's answer at the following question:
How to optimize network-queue-take in android Volley? (Volley Google IO 2013)
I think you can try increase the value of NETWORK_THREAD_POOL_SIZE by using the following constructor in your app:
public RequestQueue(Cache cache, Network network, int threadPoolSize) {
this(cache, network, threadPoolSize,
new ExecutorDelivery(new Handler(Looper.getMainLooper()))); }
P/S: if you only want the lines BasicNetwork.logSlowRequests: HTTP response for request not displayed anymore without increasing NETWORK_THREAD_POOL_SIZE, only need to comment (//) the line logSlowRequests... above (when your app uses Google's volley as a module - not jar file, not compile mcxiaoke... in build.gradle file)
Hope it helps!
public class JGet extends Request {
private final Response.Listener listener;
public JGet(final String url, List params,
Response.Listener responseListener) {
super(Request.Method.GET, NetUtils.getUrlWithParams(url, params), new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError volleyError) {
NetUtils.dealVolleyError(volleyError, url);
}
});
this.listener = responseListener;
this.setRetryPolicy(new DefaultRetryPolicy(20 * 1000, 0, 1.0f));
LogUtils.e("request-start--->");
LogUtils.e(url);
LogUtils.e(params);
LogUtils.e("request-start--->");
}
}
set timeout time.
Try not using the require statement to connect to your database when sending request to a PHP file using volley.
I've noticed a time-out only when I use something like (require "init.php")
But when I directly put my DB connection information in the same file everything seems to work just fine.
request.setRetryPolicy(new DefaultRetryPolicy( 50000, 5, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT))
I'm using Volley as my network stack in a project I'm working on in Android. Part of my requirements is to download potentially very large files and save them on the file system.
Ive been looking at the implementation of volley, and it seems that the only way volley works is it downloads an entire file into a potentially massive byte array and then defers handling of this byte array to some callback handler.
Since these files can be very large, I'm worried about an out of memory error during the download process.
Is there a way to tell volley to process all bytes from an http input stream directly into a file output stream? Or would this require me to implement my own network object?
I couldn't find any material about this online, so any suggestions would be appreciated.
Okay, so I've come up with a solution which involves editing Volley itself. Here's a walk through:
Network response can't hold a byte array anymore. It needs to hold an input stream. Doing this immediately breaks all request implementations, since they rely on NetworkResponse holding a public byte array member. The least invasive way I found to deal with this is to add a "toByteArray" method inside NetworkResponse, and then do a little refactoring, making any reference to a byte array use this method, rather than the removed byte array member. This means that the transition of the input stream to a byte array happens during the response parsing. I'm not entirely sure what the long term effects of this are, and so some unit testing / community input would be a huge help here. Here's the code:
public class NetworkResponse {
/**
* Creates a new network response.
* #param statusCode the HTTP status code
* #param data Response body
* #param headers Headers returned with this response, or null for none
* #param notModified True if the server returned a 304 and the data was already in cache
*/
public NetworkResponse(int statusCode, inputStream data, Map<String, String> headers,
boolean notModified, ByteArrayPool byteArrayPool, int contentLength) {
this.statusCode = statusCode;
this.data = data;
this.headers = headers;
this.notModified = notModified;
this.byteArrayPool = byteArrayPool;
this.contentLength = contentLength;
}
public NetworkResponse(byte[] data) {
this(HttpStatus.SC_OK, data, Collections.<String, String>emptyMap(), false);
}
public NetworkResponse(byte[] data, Map<String, String> headers) {
this(HttpStatus.SC_OK, data, headers, false);
}
/** The HTTP status code. */
public final int statusCode;
/** Raw data from this response. */
public final InputStream inputStream;
/** Response headers. */
public final Map<String, String> headers;
/** True if the server returned a 304 (Not Modified). */
public final boolean notModified;
public final ByteArrayPool byteArrayPool;
public final int contentLength;
// method taken from BasicNetwork with a few small alterations.
public byte[] toByteArray() throws IOException, ServerError {
PoolingByteArrayOutputStream bytes =
new PoolingByteArrayOutputStream(byteArrayPool, contentLength);
byte[] buffer = null;
try {
if (inputStream == null) {
throw new ServerError();
}
buffer = byteArrayPool.getBuf(1024);
int count;
while ((count = inputStream.read(buffer)) != -1) {
bytes.write(buffer, 0, count);
}
return bytes.toByteArray();
} finally {
try {
// Close the InputStream and release the resources by "consuming the content".
// Not sure what to do about the entity "consumeContent()"... ideas?
inputStream.close();
} catch (IOException e) {
// This can happen if there was an exception above that left the entity in
// an invalid state.
VolleyLog.v("Error occured when calling consumingContent");
}
byteArrayPool.returnBuf(buffer);
bytes.close();
}
}
}
Then to prepare the NetworkResponse, we need to edit the BasicNetwork to create the NetworkResponse correctly (inside BasicNetwork.performRequest):
int contentLength = 0;
if (httpResponse.getEntity() != null)
{
responseContents = httpResponse.getEntity().getContent(); // responseContents is now an InputStream
contentLength = httpResponse.getEntity().getContentLength();
}
...
return new NetworkResponse(statusCode, responseContents, responseHeaders, false, mPool, contentLength);
That's it. Once the data inside network response is an input stream, I can build my own requests which can parse it directly into a file output stream which only hold a small in-memory buffer.
From a few initial tests, this seems to be working alright without harming other components, however a change like this probably requires some more intensive testing & peer reviewing, so I'm going to leave this answer not marked as correct until more people weigh in, or I see it's robust enough to rely on.
Please feel free to comment on this answer and/or post answers yourselves. This feels like a serious flaw in Volley's design, and if you see flaws with this design, or can think of better designs yourselves, I think it would benefit everyone.
I want to clear the request queue each 30 minutes for example.
So What is the best way to clear volley cache automatically?
Override methods by extending the volley cache class?
Or build a timer which will clear the cache every times i need?
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.
Easy way to do that is override onRequestFinished method and clear cache. Or you can run inside the timer after 30 min.
final RequestQueue requestQueue = Volley.newRequestQueue(this);
requestQueue.add(stringRequest);
requestQueue.addRequestFinishedListener(new RequestQueue.RequestFinishedListener<Object>() {
#Override
public void onRequestFinished(Request<Object> request) {
requestQueue.getCache().clear();
}
});
I was trying to remove bitmap from cache by using remove(key) but it was not working, so I have check url received by putBitmap(String url, Bitmap bitmap). I found url has some prefix like #W0#H#S7http... this is because volley call getCacheKey(String url, int maxWidth, int maxHeight, ScaleType scaleType) for each url. SO if you wants to remove url from cache then you also have to call this function for getting key for url.
String key = mImageLoader.getCacheKey(url, 0, 0, ImageView.ScaleType.CENTER_INSIDE);
mRequestQueue.getCache().remove(key);
Pass 0,0 and ImageView.ScaleType.CENTER_INSIDE if you are using imageLoader.get(String requestUrl,ImageLoader.ImageListener listener) else pass min height and width and scale type you are using.
NOTE getCacheKey() is private function of ImageLoader class so you have to change it to public for using it inside you app.
The service I am using to obtain images, like many such sites does not have a cache control header indicating how long the image should be cached. Volley uses an http cache control header by default to decide how long to cache images on disk. How could I override this default behavior and keep such images for a set period of time?
Thanks
I needed to change the default caching strategy to a "cache all" policy, without taking into account the HTTP headers.
You want to cache for a set period of time. There are several ways you can do this, since there are many places in the code that "touch" the network response. I suggest an edit to the HttpHeaderParser (parseCacheHeaders method at line 39):
Cache.Entry entry = new Cache.Entry();
entry.data = response.data;
entry.etag = serverEtag;
entry.softTtl = softExpire;
entry.ttl = now; // **Edited**
entry.serverDate = serverDate;
entry.responseHeaders = headers;
and another to Cache.Entry class:
/** True if the entry is expired. */
public boolean isExpired() {
return this.ttl + GLOBAL_TTL < System.currentTimeMillis();
}
/** True if a refresh is needed from the original data source. */
public boolean refreshNeeded() {
return this.softTtl + GLOBAL_TTL < System.currentTimeMillis();
}
where GLOBAL_TTL is a constant representing the time you want each image to live in the cache.
I'm trying to cache JSON requests from a server, however, they are incorrectly using the Cache-Control header, amongst others (everything expires in the past). I want to override it so that calls are cached for say, 3 hours, regardless of what the server requests. Is that possible? The documentation for Volley is Scarce.
You might subclass the JsonObjectRequest class and overwrite parseNetworkResponse. You will notice the call to HttpHeaderParser.parseCacheHeaders - it's a good place to start :] just wrap this call or replace it and provide your own dummy Cache header configuration object [with your proprietary clientside cache time] to Response.success.
In my implementation it looks like this:
parseNetworkResponse
return Response.success(payload, enforceClientCaching(HttpHeaderParser.parseCacheHeaders(response), response));
with enforceClientCaching related members being
protected static final int defaultClientCacheExpiry = 1000 * 60 * 60; // milliseconds; = 1 hour
protected Cache.Entry enforceClientCaching(Cache.Entry entry, NetworkResponse response) {
if (getClientCacheExpiry() == null) return entry;
long now = System.currentTimeMillis();
if (entry == null) {
entry = new Cache.Entry();
entry.data = response.data;
entry.etag = response.headers.get("ETag");
entry.softTtl = now + getClientCacheExpiry();
entry.ttl = entry.softTtl;
entry.serverDate = now;
entry.responseHeaders = response.headers;
} else if (entry.isExpired()) {
entry.softTtl = now + getClientCacheExpiry();
entry.ttl = entry.softTtl;
}
return entry;
}
protected Integer getClientCacheExpiry() {
return defaultClientCacheExpiry;
}
It handles 2 cases:
no Cache headers were set
Server cache entry indicates expired item
So if the server starts sending correct cache headers with expiry in the future, it will still work.