How can I check whether Volley gets the results of a JsonObjectRequest from the cache or from the network?
I need to show a progress dialog when it needs a network connection but not when the results are quickly received from the cache.
my request looks something like this
volleyQueue = Volley.newRequestQueue(this);
JsonObjectRequest jr = new JsonObjectRequest(Request.Method.POST, url, null, new Response.Listener<JSONObject>(){...stuff}, new Response.ErrorListener(){...errorstuff});
jr.setShouldCache(true);
volleyQueue.add(jr);
I did this by overriding Request#addMarker and checking for a "cache-hit" marker being added:
public class MyRequest<T> extends Request<T> {
protected boolean cacheHit;
#Override
public void addMarker(String tag) {
super.addMarker(tag);
cacheHit = false;
if (tag.equals("cache-hit")){
cacheHit = true;
}
}
}
Before making the Request you can get the cache from the Request Queue and check if the Entry is not null.
mRequestQueue.getCache().get("key");
The key for each request is usually the URL.
I guess you should have to check if the Entry has expired too.
Volley has a built in way to know if image requests are immediate through the ImageContainer class, but it doesn't seem to have a similar mechanism for other requests such a JSON object request.
It seems that you have 2 main choices:
You can set a timer for something like 300ms after you request the JSON (test for the best time). When the timer is done, check to see if you have the result already, otherwise show the dialog. I know this is a bit of a "hack" but it could be good enough.
Edit the Volley code to add an "isImmediate" flag to every request. There are multiple ways to achieve this. I suggest starting at CacheDispatcher
Starting from Tim Kelly's answer.
by the time you check "cacheHit", it'll be reverted to false and you'll not know that it's a cache hit because many other tags are received after "cacheHit" is received and before the "onResponse" is called.
So, add
if(tag.equals("network-http-complete")){
cacheHit = false;
}
and remove cacheHit = false;
adb shell setprop log.tag.Volley VERBOSE
Run this command in your terminal, you may need to set 'adb' in your path in order to use that command, it should be located in your sdk/platform-tools/ dir.
This will provide much more detailed volley logs and will show something along the lines of an execution stack for a volley request which exhibits cache hits or misses.
Related
Volley
.newRequestQueue(context)
.add(new JsonObjectRequest(
Request.Method.POST,
BuildConfig.API_URL + "/user",
userJson,
new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
doSthOnRequestSuccess();
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
doSthOnRequestFail();
}
}));
When a request times out, ErrorListener is run, then later Volley automatically retries the request and this time it is successful. But then the success response listener is not run.
For example, I make a request when user clicks some button, request fails, so I show an error message on screen that the operation has failed. Screen is in error state now, but Volley retries the request in the background, it succeeds, but does not call the success listener code. This results in screen remaining in error state, or user clicking the button again and making a duplicate request.
I modified the api which volley calls to be able to handle duplicate requests, but is there a way to solve this on the android side?
I think if volley gives you callback after the last try. That means if you have set maximum retries of 3 and api fails for 2 and not for 3 then you will on get the onResponse() callback.
Volley returns either Error or Success per request. This means that when your onErrorResponse is called you must not expect that onResponse can be ever called.
What happens most probably is that your request with the retries failed. However the server might have processed and sent the last request form your Volley client but the data couldn't reach it on time.
In more detail:
Calls are looped in BasicNetwork class until success or exception. On particular exceptions attemptRetryOnException is called where the RetryPolicy of the request is advised whether to pass the exception or ignore it and try again. Normally this is the DefaultRetryPolicy.
From that you can see that you will receive just one final callback from Volley.
However one way to monitor those retries is to enable marker logs by:
<android-sdk>/platform-tools/adb shell setprop log.tag.Volley VERBOSE
I'm using Google's network library Volley to perform a set of operations. In particular, I'm relying on StringRequest in order to fetch the HTML of some protected pages. In order to perform the (authenticated) request, I always add to the request a set of cookies.
The problem is that, without any apparent reason, Volley sometimes gives me an empty source code! (and this WITHOUT a 204 status code: It's a pure 200).
#Override
protected Response<String> parseNetworkResponse(NetworkResponse response) {
// HERE: response.data sometimes has size 0!
// on the other hand, response.statusCode is 200.
// [...]
}
I am completely sure that this is not a problem of the remote server (when I navigate the very same page, with the same cookies, using a web browser, everything is fine). I'm also having a lot of issues regarding TimeoutError (don't know whether it's something relevant or not).
I'm REALLY tempted to switch to something written ad hoc in order to settle things once and for all, but the multithreading features that Volley implements out of the box are still a huge factor that is restraining me.
Regarding the TimeoutError, did you setting the retry policy? For example, with
stringRequest.setRetryPolicy(new DefaultRetryPolicy(
60000,
DefaultRetryPolicy.DEFAULT_MAX_RETRIES,
DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
We are trying to use the library on Android for TimedEviction. The items in the cache are expiring as soon as we overwrite an existing item.
We are building the cache as follows:
private Cache rssiMap;
RemovalListener removalListener = new RemovalListener() {
#Override
public void onRemoval(RemovalNotification removal) {
}
};
rssiMap = CacheBuilder.newBuilder()
.expireAfterWrite(1, TimeUnit.MINUTES)
.removalListener(removalListener)
.build();
rssiMap.put(device, rssi);
Is there something wrong we are doing with the code or is this a known issue?
This is correct behavior.
Actually, client code doesn't care WHEN the elements are expired, right? Client code does care about WHAT final cache values are.
The RemovalListener focuses on WHAT is evicted instead of WHEN.
JavaDoc of RemovalListener.onRemoval():
Notifies the listener that a removal occurred at some point in the
past.
By the way, I recommend use Cache.get() instead of Cache.put().
From JavaDoc of Cache.put():
Prefer get(Object, Callable) when using the conventional "if cached,
return; otherwise create, cache and return" pattern.
I have a worker thread that runs in an infinite loop. If it's queue of http requests is empty it waits. As soon as a http request is added to the queue it gets notified and executes this http request. This works all fine but I have some questions on this:
I'm doing it something like this (shortened!):
mHttpClient = new DefaultHttpClient();
mHttpPost = new HttpPost(MyHttpClient.getAbsoluteUrl(url);
while (true)
{
// Check if the queue is empty, if so -> wait
StringEntity se = new StringEntity(queue.poll());
mHttpPost.setEntity(se);
HttpResponse response = mHttpClient.execute(mHttpPost);
}
The question is: is this the most efficient way to do it if the queue has like 100 items? Does the http connection remain open all the time or does it get connected again and again? And if it remains open, is it a good idea to leave it open all the time when the app is running, or should I close it until new items are added to the queue?
The second question is concerning the infinite loop. I need the thread to run all the time when the app is running but the still the infinite loop doesn't look nice. I know I could make something like: while(!cancelled) but I don't call a thread.cancel() method anyway because I mean there is no App.onDestroy() event where I could call thread.cancel(), right? How would you handle that? Because I'd actually want to save the queue to "disk" when the thread is killed by the system but how can this be done?
Sorry for the long text and my bad english
I've run into this weird error, where some images get cached as usual and some don't, any idea why?
Both images do get displayed and memory cached just fine, but when offline some display error image.
For example, this works fine:
http://cs4381.vk.me/u73742951/a_58a41ac2.jpg
However, this does not: http://upload.wikimedia.org/wikipedia/commons/thumb/d/d7/Android_robot.svg/220px-Android_robot.svg.png
Both work fine displaying and memcaching but the second doesn't get displayed from disk cache, although I think I see it being saved, as app says it has 12kB cache in the system settings
Edit
I checked out a clean copy of Volley and it does the same thing. Its definatelly a bug...
From what Ive found out its that images do get cached, but Bitmap cachedBitmap = mCache.getBitmap(cacheKey); always returns null, so the cache says it doesnt have the bitmaps and then proceedes to download it again, and fail when offline, weird
The reason you're not getting any hits is because the default behavior in Volley for disk caching is dependent on the HTTP headers of the element you're requesting (in your case, an image).
Check the volley logs and see if you get the "cache-hit-expired" message - that means that the image was cached but it's TTL is expired as far as the default disk cache is concerned.
If you want the default settings to work, the images must have a Cache-Control header like max-age=??? where the question marks indicate enough seconds from the time it was downloaded.
If you want to change the default behavior, I'm not sure, but I think you have to edit the code a bit.
Look at the CacheDispatcher class in the Volley source.
Hope that helps.
A quick and dirty way:
private static class NoExpireDiskBasedCache extends DiskBasedCache
{
public NoExpireDiskBasedCache(File rootDirectory, int maxCacheSizeInBytes)
{
super(rootDirectory, maxCacheSizeInBytes);
}
public NoExpireDiskBasedCache(File rootDirectory)
{
super(rootDirectory);
}
#Override
public synchronized void put(String key, Entry entry)
{
if (entry != null)
{
entry.etag = null;
entry.softTtl = Long.MAX_VALUE;
entry.ttl = Long.MAX_VALUE;
}
super.put(key, entry);
}
}