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));
Related
The google demo makes a single API request. I have tried to use this code as the basis for a more real world scenario (multiple different API calls) but keep hitting a brick wall. The demo's structure makes it very difficult.
Consider the code that makes the API request:
private void getResultsFromApi() {
if (! isGooglePlayServicesAvailable()) {
acquireGooglePlayServices();
} else if (mCredential.getSelectedAccountName() == null) {
chooseAccount();
} else if (! isDeviceOnline()) {
mOutputText.setText("No network connection available.");
} else {
new MakeRequestTask(mCredential).execute();
}
}
getResultsFromApi() does a lot of auth stuff before the API request. Should this be done before every API request??
getResultsFromApi() may start an activity (in chooseAccount())which means that the onActivityResult() will (re)call getResultsFromApi() in order to eventually get to the API request.
In fact there are actually 5 calls to getResultsFromApi() in the code which together achieve the auth for the single API request!! It may be necessary but it ends up being spaghetti.
This makes the demo impossible to generalize in its current form. Say I want a 2nd button making some other API request. Cant be done without a major rewrite
Can anyone suggest how I can reorganise the demo code in order to make it useable in the real world?
Thanks
getResultsFromApi does not do "a lot of stuff" it just checks that everything is done correctly as said in the demo project :
Attempt to call the API, after verifying that all the preconditions are
satisfied
If a condition is not satisfied another function will be called to solve this, like to select an account. Then getResultsFromApi will be called again to finish that call that you started with not all the preconditions satisfied.
You are not force to do all the checks, but if a precondition is not satisfied your call will fail.
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 have an Android app that uses Google Volley to make requests to a web server backend (http://www.example.com/request.php). The code that requests the webpage is called when the user moves a map.
The issue is that sometimes the request fails. When it fails, I open my Chrome browser (also on the phone), and navigate to that "request.php" backend page. The request fails with a connection timeout error. Then I press refresh in Chrome, and it loads correctly. I reopen my app, and then everything works fine again, ie: the request goes through.
Steps I did to debug:
I switched from the com.loopj Async library to Google Volley -> still has timeout errors
Requested a different page (eg: stackoverflow.com instead of example.com/request.php) when the map is moved -> no errors
Added this line to htaccess on the server: RewriteRule (.*) - [E=Cache-Control:no-cache] because I had problems with x-litespeed-cache serving cached pages before -> still has timeout errors
Tried on multiple devices and Android emulators -> still has timeout errors randomly
Filed a ticket with my web host asking if they noticed any timeout errors from my page -> they said no
Here's my code:
public void updateMarkers() {
params = new HashMap<String, String>();
params.put("mykey", "myvalue");
StringRequest fpsr = new StringRequest(Method.POST, "http://www.example.com/request.php", new Response.Listener<String>() {
#Override
public void onResponse(String response) {
//do stuff with the response
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
//do stuff with error
}
}) {
#Override
protected Map<String, String> getParams() {
return params;
}
};
ApplicationController.getInstance().addToRequestQueue(fpsr, "updateMarkers");
}
And the ApplicationController.java is the same as provided here: http://www.androidhive.info/2014/05/android-working-with-volley-library-1/
I highly doubt the code has problems, since requests to pages other than my own never have problems.
The strange thing is, my web host said they didn't notice any timeout errors. But even visiting the page in Chrome for Android had timeout errors that only went away when I pressed refresh.
Can anyone shed light on this? Thank you very much!
Also, I don't have the HTTP response code from the VolleyError. The reason is that it fails randomly, and I don't always have the phone attached to a computer to see the logcat. Also, I tried logging the error.networkResponse.statusCode before; it crashed the app due to a null pointer exception.
If an Android Volley post request fails due to network loss, will Android Volley retry the post after the network connection is restored automatically? Will it fire all of the request attempts, wait for connection to be reestablished or simply trigger an error and stop?
If Android Volley doesn't retry after a connection is reestablished, it seems I will have to create logic so that I have an extra queue for whenever the connection gets lost, and that will retry whenever connection state changes.
As per this link:
There is no direct way to specify request timeout value in Volley, but there is a workaround, you need to set a RetryPolicy on the request object. The DefaultRetryPolicy class takes an argument called initialTimeout, this can be used to specify a request timeout, make sure the maximum retry count is 1 so that volley does not retry the request after the timeout has been exceeded.
Setting Request Timeout:
request.setRetryPolicy(new DefaultRetryPolicy(20 * 1000, 1, 1.0f));
If you want to retry failed requests (due to timeout) you can specify that too using the code above, just increase the retry count. Note the last argument, it allows you to specify a backoff multiplier which can be used to implement “exponential backoff” that some RESTful services recommend.
The link has a lot of useful tips and tricks for using Volley. Hope this helps!
If an Android Volley post request fails due to network loss, will Android Volley retry the post after the network connection is restored automatically?
No, it won't. I might not even be desired depending on your application.
Will it fire all of the request attempts, wait for connection to
reestablish or simply trigger an error and stop?
It simply throws an error. And yes, you should write this kind of logic yourself.
In case an IOException appears (e.g. java.net.ConnectException), Volley does not use the retry policy.
Volley uses only the retry policy in case of SocketTimeoutException, ConnectTimeoutException or if the HTTP response code is 401 (forbidden) or 302 (moved permanently).
if you use (AsyncHttpClient) you can try call this methode :
setMaxRetriesAndTimeout(int retries, int timeout)
Sets the maximum number of retries and timeout for a particular Request.
*
* #param retries maximum number of retries per request
* #param timeout sleep between retries in milliseconds
*/
I have this problem since I try to volley request on a method and onErrorResponse method of volley call that method again.example:
#Override
public void onErrorResponse(VolleyError volleyError) {
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
sendOTPAgain();//this method call again and again
}
}, 1000);
}
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.