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.
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));
I'm using Azure Mobile Services in my android application to add authentication to the app, via Facebook and Google. However, every single time I attempt to login from the app, I receive the following error:
"com.microsoft.windowsazure.mobileservices.MobileServiceException: Logging >in with the selected authentication provider is not enabled".
No other errors occur. This is my code:
private void authenticate(boolean bRefreshCache)
throws ClientProtocolException, IOException {
bAuthenticating = true;
if (bRefreshCache || !loadUserTokenCache(mClient)) {
mClient.login(MobileServiceAuthenticationProvider.Facebook,
new UserAuthenticationCallback() {
#Override
public void onCompleted(MobileServiceUser user,
Exception exception,
ServiceFilterResponse response) {
synchronized (mAuthenticationLock) {
if (exception == null) {
cacheUserToken(mClient.getCurrentUser());
Log.i("MappingRoadConditions",
"authenticating");
createAndShowDialog(String.format(
"You are now logged in - %1$2s",
user.getUserId()), "Success");
} else {
createAndShowDialog(exception.getMessage(),
"Login Error");
}
bAuthenticating = false;
mAuthenticationLock.notifyAll();
}
}
});
} else {
// Other threads may be blocked waiting to be notified when
// authentication is complete.
synchronized (mAuthenticationLock) {
bAuthenticating = false;
mAuthenticationLock.notifyAll();
}
}
}
The function for logging in by Google is exactly the same, other than the name of the provider of course.
1) I have tried troubleshooting by logging in through the browser and I can login perfectly well using both Facebook and Google.
2) I have added the internet permission in the manifest file.
3) I have also tried testing the app by changing the internet connections, in case it's a network connection problem but to no avail. I am able to login perfectly well through the browser on the same internet connection.
Any ideas on what could be happening?
I struggled with this for a while when moving my working code over into a fresh app
It seems that after I eliminated the provider app connection as your problem (I used the javascript html client in parallel ) I needed to go back to basics because I found this similar question
Check your Manifest
I also had this issue just happen on a successful build - the ADB bridge had failed and the emulator could not connect to the internet (I had switched networks)
This error code is not descriptive, but Azure seems to assume if it can't connect to a provider then you didn't set it up!
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.
I have a webview application that uses hooks to execute native java code (ie populate a local db) and to catch these hooks I use the onLoadResource function.
It works as expected, but about 10% of the time I get server log errors of the hook being fired and its clogging up my logs with "not found" errors.
So it basically works like this:
User loads their webview app
In the app they click on of the hooks (http://domain.com/hook/datatopass)
The onLoadResource does its processing and forwards the user to
another page (http://domain.com/home)
The majority of the time it works, but sporadically I get the "ERROR [http://domain.com/hook/datatopass] not found" error.
From what I can tell the user doesn't see any error pages, they get forwarded to the correct place - but I don't know why the onLoadResource doesn't catch every request before it logs an error. Anyone know how to avoid these errors being thrown, and why this is happening?
It seems for me that a Timeout occurs - sometimes.
Have you tried to set the timeout time higher ?
Or have you tried to show an ProgressDialog like below to locate the problem?
public void onLoadResource(WebView view, String url) {
// Check to see if there is a progress dialog
if (progressDialog == null) {
// If no progress dialog, make one and set message
progressDialog = new ProgressDialog(activity);
progressDialog.setMessage("Loading please wait...");
progressDialog.show();
// Hide the webview while loading
webview.setEnabled(false);
}
}