i want to call 3 service methods (A B and C) back to back. The important point is B must be called after response received from A and same situation between B and C as well. I add all of requests to queue using RequestQueue.add(...). But now request B is called before receiving response from A. Is it possible to manage this using volley library.
I know i can do request B after receiving response from A but i want to know can volley does this work.
You can implement your own Response listener so you can call A to B and B to C in the response callback method.
There is a simple example here: https://stackoverflow.com/a/17278867/508126
Volley can't do it itself but it can do it if you implement Response.Listener and add your logic in it
You can't give request an order, but you can make them run one after another. For this you need to implement your own RequestQueue.
Here is sample which demonstrates how to make all your requests execute in the same order, in which you added them to queue, since it uses single thread execution.
// Copied from Volley.newRequestQueue(..); source code
File cacheDir = new File(context.getCacheDir(), "def_cahce_dir");
String userAgent = "volley/0";
try {
String packageName = context.getPackageName();
PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0);
userAgent = packageName + "/" + info.versionCode;
} catch (PackageManager.NameNotFoundException e) {
}
if (stack == null) {
if (Build.VERSION.SDK_INT >= 9) {
stack = new HurlStack();
} else {
// Prior to Gingerbread, HttpUrlConnection was unreliable.
// See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html
stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
}
}
int threadPoolSize = 1; // means only one request at a time
RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network, threadPoolSize);
queue.start();
Related
Multiple invocations to the jsonRequest() method from different Threads (Timer-1 and EDT) do interfere with each other and even one call returns the result of a previous invocation
My CodeNameOne application uses a background Thread (Timer-1) to retrieve and display data from a REST service every second and it allows the user to issue commands that also issue REST calls from the EDT thread.
private Map<String, Object> jsonRequest(String url, String body, String cmd, int timeoutMs) {
long startTs = System.currentTimeMillis();
try {
request = new ConnectionRequest();
request.setReadResponseForErrors(true);
// request.setTimeout(timeoutMs);
// Shai: Timeout in Codename One is currently limited to connection timeout and
// doesn't apply to read timeout so once a connection is made it will last
request.setHttpMethod(cmd);
request.setPost(cmd.equalsIgnoreCase("POST") || cmd.equalsIgnoreCase("PUT") || cmd.equalsIgnoreCase("PATCH"));
if (body != null) {
request.addRequestHeader("Accept", "application/json");
request.setContentType("application/json");
request.setRequestBody(body);
request.setWriteRequest(true);
}
request.setUrl(url);
NetworkManager.getInstance().addToQueueAndWait(request);
long duration = System.currentTimeMillis() - startTs;
Log.p(cmd + ": " + url + " " + duration + " ms");
if (request.getResponseCode() >= 400 || request.getResponseData() == null) {
Log.p("responseCode=" + request.getResponseCode() + " responseData=" + request.getResponseData());
return null;
}
Log.p(cmd + ": " + url + " " + new String(request.getResponseData()));
Map<String, Object> result = new JSONParser().parseJSON(new InputStreamReader(new ByteArrayInputStream(request.getResponseData()), "UTF-8"));
return result;
} catch (Exception e) {
problemHandler.handle(cmd, url, e);
}
return null;
}
Actually result of multiple invocations get mixed up.
I would expect that each call to addToQueueAndWait() waits for the right result and returns just when the result is there.
I observed this problem to happen much more often on Android than on iOS or the simulator
I doubt that's what you are seeing. I see that request is defined in the class level as a variable so I'm guessing you are seeing a typical race condition where the request variable gets replaced while one is sent and by the time you reach the parsing it's a different object.
There is no need to use a thread for polling as networking already runs on a separate thread (or more this is usually determined in the init(Object) method).
I would suggest using a timer for a single invocation that's invoked after the response finishes.
A better approach would be websockets though: https://www.codenameone.com/blog/introducing-codename-one-websocket-support.html
With websockets the server can push out an update notification. This will save you the need to constantly poll the server. It saves on device battery life and resources on server/device.
Second Activity in my project starts very slow, and I don't know how to fix it. How it works (everything's in onCreate()): firstly it does get-request to the page with json:
try {
DefaultHttpClient hc = new DefaultHttpClient();
ResponseHandler<String> res = new BasicResponseHandler();
HttpGet getMethod = new HttpGet(url);
String response = hc.execute(getMethod, res);
resStr = response.toString();
} catch (Exception e) {
System.out.println("Exp=" + e);
}
I know some methods from here are deprecated, but I don't know how to make it more optimized. As a result it returns a string with JSON array of about 32 Objects.
Then I fill 6 arrays to pass them to ArrayAdapter for filling ListView. Methods of getting different types of data looks like this:
public static String GetWantedType(String resStr, int num) {
String jsonvalues = "";
try {
JSONArray json_Array = new JSONArray(resStr);
JSONObject json_data = json_Array.getJSONObject(num);
jsonvalues = json_data.getString("wanted_type");
} catch (JSONException e) {
e.printStackTrace();
}
return jsonvalues;
}
Maybe I should have created json_Array one time outside filling arrays and just pass it to those methods as a JSONArray, not as a string - I don't know if it influence the speed.
In the ArrayAdapter everything seems to be all right, I'm using ViewHolder and my scrolling is excelent. Unlike starting Activity.
How to make it better and more optimized?
First, don't do all operations in onCreate() , prefer to do in onResume()
Second, all server call should be in background thread using Async and use result for displaying user.
If you don't want to call multiple times API for data during onResume() and onPause(), you can consume result of data in array or something and when onResume() call, you can check whether it has data, just load it, else fetch from server.
As Gaurav said the problem is that the network request is called on the main thread.
At the time you ask a network call your program say : OK STOP I WAIT THE RESPONSE.
So if you want to change this you can do various things.
For example you can use a Asynchronous Network call with a lib (loopj lib)
or you can simply open a Thread : do the network call.
With that your UI will not freeze
Are you doing the network call on the main thread? Please don't do it. Instead do the network operations on different thread.
For networking you can use a library retrofit.
It also makes it easy for you do the operations asynchronously, by using callbacks or using Observables from RxJava
How i can change the User-Agent in request header with the app-name in first section of user-agent like this :
Myappname (Linux; U; Android 4.3; Galaxy Nexus Build/JWR66Y)
note that i don't want to change other parameters in user-agnet string
i used volley for requests.
any suggestion to do it dynamically?
I wanted to do EXACTLY the same thing in my Android app. Namely, modify the User-Agent header but only append/prepend something and leave the "original" content (or most of it).
The problem with #athor answer is that, if look carefully, volley only uses the userAgent string when it uses HttpClientStack, and that is for devices using Android API Level 8 lower (in other words, in the minority of cases). For API Levels 9 or higher, it uses HurlStack, or, a stack or your own, if you are passing it as parameter for newRequestQueue, which in my case, and probably in most cases, is a subclass of HurlStack, for example OkHttpStack.
These, make no use of the userAgent string you see there, and, as far as I know, don't offer a way to set the user agent.
What I ended up doing was, in one of my subclasses of Volley's Request<T>, I overrode the getHeaders method, and made the necessary adjustment to the User-Agent header. Here's the code snippet that prepends the app's name and version to the original User-Agent header.
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
Map<String, String> headers = super.getHeaders();
if (headers == null || headers.equals(Collections.emptyMap())) {
headers = new HashMap<String, String>();
}
if (context != null) {
StringBuilder label = new StringBuilder();
label.append(context.getApplicationInfo().loadLabel(context.getPackageManager()));
label.append("/");
try {
PackageInfo pInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
label.append(pInfo.versionName);
} catch (NameNotFoundException e) {
}
label.append(" ");
label.append(System.getProperty("http.agent"));
headers.put("User-Agent", label.toString());
}
return headers;
}
Note that I'm using System.getProperty("http.agent") to access the "original" User-Agent header, as I found no other way to that. Of course, you could try to modify this string to remove the Dalvik/1.6.0 but I don't know if that's 100% safe.
Also, note that I'm using Android Context to read app name and version.
Hope this helps!
Volley sets up the user agent in the newRequestQueue method.
public static RequestQueue newRequestQueue(Context context, HttpStack stack) {
File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);
String userAgent = "volley/0";
try {
String packageName = context.getPackageName();
PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0);
userAgent = packageName + "/" + info.versionCode;
} catch (NameNotFoundException e) {
}
if (stack == null) {
if (Build.VERSION.SDK_INT >= 9) {
stack = new HurlStack();
} else {
// Prior to Gingerbread, HttpUrlConnection was unreliable.
// See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html
stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
}
}
Network network = new ManualProxyNetwork(new BasicNetwork(stack));
RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
queue.start();
return queue;
}
A simple solution is just to copy this method to your application, and modify the user agent string to use your app name instead of the package name. It would be a simple 1 line change.
Then just call your custom newRequestQueue method instead of the volley method.
I recently started to use Volley lib from Google for my network requests. One of my requests get error 301 for redirect, so my question is that can volley handle redirect somehow automatically or do I have to handle it manually in parseNetworkError or use some kind of RetryPolicyhere?
Thanks.
Replace your url like that url.replace("http", "https");
for example:
if your url looking like that : "http://graph.facebook......." than
it should be like : "https://graph.facebook......."
it works for me
I fixed it catching the http status 301 or 302, reading redirect url and setting it to request then throwing expection which triggers retry.
Edit: Here are the main keys in volley lib which i modified:
Added method public void setUrl(final String url) for class Request
In class BasicNetwork is added check for redirection after // Handle cache validation, if (statusCode == HttpStatus.SC_MOVED_PERMANENTLY) || statusCode == HttpStatus.SC_MOVED_TEMPORARILY), there I read the redirect url with responseHeaders.get("location"), call setUrl with request object and throw error
Error get's catched and it calls attemptRetryOnException
You also need to have RetryPolicy set for the Request (see DefaultRetryPolicy for this)
If you dont want to modify the Volley lib you can catch the 301 and manually re-send the request.
In your GsonRequest class implement deliverError and create a new Request object with the new Location url from the header and insert that to the request queue.
Something like this:
#Override
public void deliverError(final VolleyError error) {
Log.d(TAG, "deliverError");
final int status = error.networkResponse.statusCode;
// Handle 30x
if(HttpURLConnection.HTTP_MOVED_PERM == status || status == HttpURLConnection.HTTP_MOVED_TEMP || status == HttpURLConnection.HTTP_SEE_OTHER) {
final String location = error.networkResponse.headers.get("Location");
Log.d(TAG, "Location: " + location);
final GsonRequest<T> request = new GsonRequest<T>(method, location, jsonRequest, this.requestContentType, this.clazz, this.ttl, this.listener, this.errorListener);
// Construct a request clone and change the url to redirect location.
RequestManager.getRequestQueue().add(request);
}
}
This way you can keep updating Volley and not have to worry about things breaking.
Like many others, I was simply confused about why Volley wasn't following redirects automatically. By looking at the source code I found that while Volley will set the redirect URL correctly on its own, it won't actually follow it unless the request's retry policy specifies to "retry" at least once. Inexplicably, the default retry policy sets maxNumRetries to 0. So the fix is to set a retry policy with 1 retry (10s timeout and 1x back-off copied from default):
request.setRetryPolicy(new DefaultRetryPolicy(10000, 1, 1.0f))
For reference, here is the source code:
/**
* Constructs a new retry policy.
* #param initialTimeoutMs The initial timeout for the policy.
* #param maxNumRetries The maximum number of retries.
* #param backoffMultiplier Backoff multiplier for the policy.
*/
public DefaultRetryPolicy(int initialTimeoutMs, int maxNumRetries, float backoffMultiplier) {
mCurrentTimeoutMs = initialTimeoutMs;
mMaxNumRetries = maxNumRetries;
mBackoffMultiplier = backoffMultiplier;
}
Alternatively, you can create a custom implementation of RetryPolicy that only "retries" in the event of a 301 or 302.
Hope this helps someone!
End up doing a merge of what most #niko and #slott answered:
// Request impl class
// ...
#Override
public void deliverError(VolleyError error) {
super.deliverError(error);
Log.e(TAG, error.getMessage(), error);
final int status = error.networkResponse.statusCode;
// Handle 30x
if (status == HttpURLConnection.HTTP_MOVED_PERM ||
status == HttpURLConnection.HTTP_MOVED_TEMP ||
status == HttpURLConnection.HTTP_SEE_OTHER) {
final String location = error.networkResponse.headers.get("Location");
if (BuildConfig.DEBUG) {
Log.d(TAG, "Location: " + location);
}
// TODO: create new request with new location
// TODO: enqueue new request
}
}
#Override
public String getUrl() {
String url = super.getUrl();
if (!url.startsWith("http://") && !url.startsWith("https://")) {
url = "http://" + url; // use http by default
}
return url;
}
It worked well overriding StringRequest methods.
Hope it can help someone.
Volley supports redirection without any patches, no need for a separate fork
Explanation:
Volley internally uses HttpClient which by default follows 301/302 unless specified otherwise
From: http://hc.apache.org/httpcomponents-client-4.2.x/tutorial/html/httpagent.html
ClientPNames.HANDLE_REDIRECTS='http.protocol.handle-redirects': defines whether redirects should be handled automatically. This parameter expects a value of type java.lang.Boolean. If this parameter is not set HttpClient will handle redirects automatically.
ok, im a bit late to the game here, but i've recently been trying to achieve this same aspect, so https://stackoverflow.com/a/17483037/2423312 is the best one, given that you are willing to fork volley and maintain it and the answer here : https://stackoverflow.com/a/27566737/2423312 - I'm not sure how this even worked.This one is spot on though : https://stackoverflow.com/a/28454312/2423312. But its actually adding a new request object to the NetworkDipatcher's queue, so you'll have to notify the caller as well somehow, there is one dirty way where you can do this by not modifying the request object + changing the field "mURL", PLEASE NOTE THAT THIS IS DEPENDENT ON YOUR IMPLEMENTATION OF VOLLEY'S RetryPolicy.java INTERFACE AND HOW YOUR CLASSES EXTENDING Request.java CLASS ARE, here you go : welcome REFLECTION
Class volleyRequestClass = request.getClass().getSuperclass();
Field urlField = volleyRequestClass.getDeclaredField("mUrl");
urlField.setAccessible(true);
urlField.set(request, newRedirectURL);
Personally I'd prefer cloning volley though. Plus looks like volley's example BasicNetwork class was designed to fail at redirects : https://github.com/google/volley/blob/ddfb86659df59e7293df9277da216d73c34aa800/src/test/java/com/android/volley/toolbox/BasicNetworkTest.java#L156 so i guess they arent leaning too much on redirects, feel free to suggest/edit. Always looking for good way..
I am using volley:1.1.1 with https url though the request was having some issue. On digging deeper i found that my request method was getting changed from POST to GET due to redirect (permanent redirect 301). I am using using nginx and in server block i was having a rewrite rule that was causing the issue.
So in short everything seems good with latest version of volley. My utility function here-
public void makePostRequest(String url, JSONObject body, final AjaxCallback ajaxCallback) {
try {
JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(Request.Method.POST,
url, body, new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
Log.d(LOG, response.toString());
ajaxCallback.onSuccess(response);
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Log.e(LOG, error.toString());
ajaxCallback.onError(error);
}
});
singleton.getRequestQueue().add(jsonObjectRequest);
} catch(Exception e) {
Log.d(LOG, "Exception makePostRequest");
e.printStackTrace();
}
}
// separate file
public interface AjaxCallback {
void onSuccess(JSONObject response);
void onError(VolleyError error);
}
I'm trying to get the user's cover photo and show it at the top of a layout. I'm using AsyncTask to run the API call to Facebook. The code I'm using to get the Facebook data is
JSONObject json = null;
response = Utility.facebook.request("me?fields=cover");
json = Util.parseJson(response);
The exception that stops the thread comes from a json error on the next step because the returned json is empty, even though the request clears through. I can get a proper json using just "me" or "me/albums" or anything other than "me?fields=cover". When I comment out the last line, 'try' process finishes with no exceptions/errors.
Is there something wrong with the Facebook API or am I doing something wrong?
I personally prefer using FQL when dealing with User Profile. If you would like to give FQL a try, check the following piece of code. If you would like to stick to Graph API, see this answer: https://stackoverflow.com/a/12434640/450534
try {
String query = "SELECT pic_cover FROM user where uid = " + PUT_THE_USER_ID_HERE;
Bundle param = new Bundle();
param.putString("method", "fql.query");
param.putString("query", query);
String response = Utility.mFacebook.request(param);
JSONArray JAUser = new JSONArray(response);
for (int i = 0; i < JAUser.length(); i++) {
JSONObject JOUser = JAUser.getJSONObject(i);
// COVER PHOTO
if (JOUser.has("pic_cover")) {
String getCover = JOUser.getString("pic_cover");
if (getCover.equals("null")) {
String finalCover = null;
} else {
JSONObject JOCoverSource = JOUser.optJSONObject("pic_cover");
if (JOCoverSource.has("source")) {
String finalCover = JOCoverSource.getString("source");
} else {
String finalCover = null;
}
}
} else {
String finalCover = null;
}
}
} catch (Exception e) {
// TODO: handle exception
}
The above code already accounts for User's who do not have a Cover Photo set in their profiles and checks for its availability. With this code, you will have the URL to the Cover Photo and can then process it as you prefer.
NOTE: If you are fetching the logged in users cover photo, this piece of code SELECT pic_cover FROM user where uid = " + PUT_THE_USER_ID_HERE; can also be written as: SELECT pic_cover FROM user where uid = me()"; For the non-logged in user's cover photo, the above can be used as is.
Couple of things as a side note.
I use Fedor's Lazy Loading technique to load images in almost exclusively.
I recommend running the code block, mine or any other solution you choose, in an AsyncTask.
The reason for not getting any result can be found in the javadoc of request(String graphPath) method:
(...) this method blocks waiting for a network response, so do not
call it in a UI thread.
In your case, you should probably do the following synchronous call:
Bundle params = new Bundle();
params.putString("fields", "cover");
String result = Utility.facebook.request("me/", params);
Siddharth Lele is very correct in his answer, but I wanted to specify the actual reason for not getting any response in this case.
Note: Fetching Cover Photo using Facebook API and endpoint https://graph.facebook.com/me?fields=cover no longer works as on 20th Dec 2014.
It was supposed to give following response:
{
"cover": {
"cover_id": "10151008748223553",
"source": "http://sphotos-a.ak.fbcdn.net/hphotos-ak-ash4/s720x720/391237_10151008748223553_422785532_n.jpg",
"offset_y": 0
},
"id": "19292868552"
}
But now it just gives User's id:
{
"id": "19292868552"
}
Verified this using Graph Tool explorer v2.2 using me?fields=cover.