Is there a way to send and listen for volley batch requests and responses in android?
i want to sync all the offline data once the network is back.
Currently am using a for loop to add the requests to the queue.I get responses individually for each request.
Is there any way for batch request and response listener?
I had a same problem then I resolved it by a little stupid way.It's only useful if you have a small number of requests in a batch. Hope it help :)
public static void requestData(#NonNull final Context context, final String url, final String cityID, final int typeOfResult, final int batchId) {
MySingletonWraper.getInstance(context).addToRequestQueue(new JsonObjectRequest(Request.Method.GET, url, null, new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
// add json object to list json of this batch
Batch batch = listBatch().get(batchID);
batch .getListRespones().add(respone);
// listBatch is a ArrayList of Object Batch which have id, queue url, list results
urlsQueue = batch .getUrlsQueue();
if (urlsQueue.isEmpty()) { // I create a queue for control in a batch, check if it's empty - this is last request
// parse JsonObject for today weather
parseJsonToWeatherObject(typeOfResult);
return;
}
// only request if sure the previous request is successed
// if have too many reuqests in a batch, just break it if you can
requestData(context, urlsQueue .poll(), cityID, typeOfResult, batchID);
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
// write log error
error.printStackTrace();
// refresh this batch
listBatch().refresh();
// re-request
fetch(context, cityID, batchID);
}
}));
}
I don't think there is realistic implementation of a batch listener. Although it is possible to send multiple requests, responses don't arrive at a same time. It might be important to note that in Volley, network pool size is capped at 4 ( I believe this is defined by the constant DEFAULT_NETWORK_THREAD_POOL_SIZE ). You may add many requests to queue but only 4 get processed at a time. And up to now, Volley doesn't have a callback for completed queue which would technically be a close enough implementation of a batch listener.
So you would still need to work on each response individually it seems. Although considering that network process is expensive, I'd work on the server to pre-process whatever data to be sent to the devices, avoiding the need to do multiple requests.
Related
I am using volley package to retrieve data from a website(JSON).
Here is my method
private void getEarthquakeList(){
// ...
// Instantiate the RequestQueue.
RequestQueue queue = Volley.newRequestQueue(this);
//Earthquake Feeder
String url ="https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/significant_month.geojson";
// Request a string response from the provided URL.
StringRequest stringRequest = new StringRequest(Request.Method.GET, url,
new Response.Listener<String>() {
#Override
public void onResponse(String response) {
Log.d("Response is: ",response);
//Parsing Json
parseJson(response);
final ListView earthquakeListView = (ListView)findViewById(R.id.list);
//Sort the array according to magnitude
earthquakeArrayList.sort((a, b) -> Double.compare(b.getTime(), a.getTime()));
mAdapter = new EarthquakeAdapter(getApplicationContext(), earthquakeArrayList);
earthquakeListView.setAdapter(mAdapter);
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Log.d("Error",error.getMessage());
}
});
// Add the request to the RequestQueue.
queue.add(stringRequest);
}
The problem is that right now I am updating the UI inside this method after it returns the response.
These are the lines
//Sort the array according to a date
earthquakeArrayList.sort((a, b) -> Double.compare(b.getTime(), a.getTime()));
mAdapter = new EarthquakeAdapter(getApplicationContext(), earthquakeArrayList);
earthquakeListView.setAdapter(mAdapter);
While I am running in the MainActivity there is no issue, the user opens the app and gets the list of earthquakes.
The issues start when I want to switch to service where I monitor every couple of minutes or when the website content is changing.
So I want my method without updating the UI in it.
The problem is that If I am not updating the UI inside onResponse, the UI thread continues and results in an empty array.
So this array stay empty earthquakeArrayList if I am not doing it inside
public void onResponse(String response)
Ideas how to separate the two, in one hand running the method and fetching the data and on the other hand the main thread will be able to access the data and not finish executing.
Thanks
EG
The recommended solution right now is to use LiveData. LiveData works on the Observer Pattern, so you can have any number of observers to the updated data both in the Service and the Activity but they need to be under the same context. These are a bit advanced topics but a good way to decouple your UI with the data layer. You can go through these two links
Google IO 2018 session on Livedata
Medium Article on LiveData
I have to make several requests to an API for an Android App and I am having the following problem:
I have a list of items and I have to make a request for each in order to get some data and show it in the app. When my list's size is 1 (in case I have only one item), the app works perfectly but if I have more I get the data mixed and one item have the value of other and things like that.
I am using JsonObjectRequest and Volley. I use callback interfaces for sending back the request data to the activities. I think it's probably a synchronisation problem but I'm not sure and I'm a bit frustrated. I've tried everything!
Request Code:
JsonObjectRequest request = new JsonObjectRequest(Request.Method.GET, urlMarket, null, new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
try {
JSONObject singleResult;
singleResult = response.getJSONArray("result").getJSONObject(0);
coin.setHigh(singleResult.getDouble("High"));
coin.setLow(singleResult.getDouble("Low"));
coin.setLast(singleResult.getDouble("Last"));
coin.setVolInBtc(singleResult.getDouble("BaseVolume"));
coin.setBid(singleResult.getDouble("Bid"));
coin.setAsk(singleResult.getDouble("Ask"));
coin.setPrevDay(singleResult.getString("PrevDay"));
callback.onSuccess(coin);
} catch (Exception e) {
e.printStackTrace();
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
error.printStackTrace();
}
});
requestQueue.add(request);
Any kind of help would be appreciated!!
PD.: if there's a better way for doing that instead of JsonObjectRequest, please tell me!
Consider either of these two options:
Extend JSONObjectRequest that accepts a id/token field which is returned as part of the Success/Error callback.
Use a final field as an identifier before you make an API call. Assuming you are making these calls in a loop, you can then refer to this final field from your anonymous callbacks to identify the specific Stock. For example, you can use the ISIN, database ID or ticker symbol to identify the response.
I am trying to get the response from instagram api using volley, but can't get the data. I did't receive any call back methods like onResponse or onErrorResponse. Nothing show up. I Could not see any error.
here is my code.
public String getUserId(String usrName) {
url = TContants.urlBeforeUserId + usrName + TContants.urlAfterUser;
JsonObjectRequest jsonObjReq;
jsonObjReq = new JsonObjectRequest(Method.GET, url,
null, new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
TagsResponse gsonData = gson.fromJson(response.toString(), TagsResponse.class);
userId = gsonData.data[0].id.toString();
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Log.e("Volley:", "getUserId response error");
}
});
AppController.getInstance().addToRequestQueue(jsonObjReq,
TContants.tag_json_obj);
return userId;
}
url is working fine, I tested it.
When code running JsonObjectRequest creating. but next step it skip the onResponse and onErrorResponse methods. plz help.
The code inside onResponse is not skipped, that's how it's supposed to work, what you are looking at is a Callback.
A very quick and general explanation would be:
this code does not run serially, instead, onResponse in this case is your implementation of an interface provided by the request object, that will be called when the response arrives, this might take several milliseconds to seconds (depending on the server, since this callback is for a network operation).
Read about callback handling (both network, and the simple ones you create with interfaces - and if you haven't yet - read about interfaces), as it is a major part of programming.
ADDITION:
To see when the response does return, I would print all the parameters before sending them to make sure they are sent correctly, and also print the response itself (response.toString() at the beginning of onResponse) and wait a bit to see it after the request is sent.
(don't be alarmed if the print won't contain readable info, it depends on the implementation of the .toString() method, for now it's just an indication that you got a response at all).
I want to send two different requests and handle two different responses in one Activity using Volley library.
My activity implements onResponseListener, so i have only one onResponse method and both responses are handled here. As they are completely same in structure i cant tell which is which.
How can i tell from which request i have received the response so i can handle them differently? Is there a way to "tag" a request or something like that?
I could set some kind of check variable, e.g. boolean firstRequestIsSent when i send the request, and then check it in the onResponse method, but its a pretty ugly solution.
Many thanks
Instead of implementing onResponse as part of the class, you can instantiate a new Response.Listener with the request. This way you will have a separate listener for each request.
StringRequest stringRequest = new StringRequest(Request.Method.GET, url,
new Response.Listener() {
#Override
public void onResponse(String response) {
// individual response here
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
// error here
}
});
I'm learning how to use Google Volley these days. It's very convenient for fast networking. It seems that all the requests are running in background in Volley. For example:
volleyRequestQueue.add(new JsonObjectRequest(Method.POST, SIGNUP_URL, reqBody, new SignUpResponseListener(), new MyErrorListener()));
Using the above code, we can make a POST call which runs in background(non-blocking way). Now my question is :
Is it possible to make the POST call in the blocking way? Why I need a blocking way to make a REST call? Because some calls, like sign in, should be done before doing something else.
Thanks
Volley supports blocking request via RequestFutures. You create a normal request but set its callbacks as your request future, which is just volley's extension of a standard java futures. The call to future.get() will block.
It looks something like this
RequestFuture<JSONObject> future = RequestFuture.newFuture();
JsonObjectRequest request = new JsonObjectRequest(Method.POST, SIGNUP_URL, reqBody, future, future)
volleyRequestQueue.add(request);
try {
JSONObject response = future.get();
} catch (InterruptedException e) {
} catch (ExecutionException e) {
}
Here's a clearer answer which handles InterruptedException properly as well as timeout. Note that the only time you would want to swallow the interrupt and continue is if you specifically intend to use the interrupt to cancel responding to the request.
RequestFuture<JSONObject> future = RequestFuture.newFuture();
JsonObjectRequest request = new JsonObjectRequest(Method.POST, SIGNUP_URL, reqBody, future, future);
volleyRequestQueue.add(request);
try {
JSONObject response = null;
while (response == null) {
try {
response = future.get(30, TimeUnit.SECONDS); // Block thread, waiting for response, timeout after 30 seconds
} catch (InterruptedException e) {
// Received interrupt signal, but still don't have response
// Restore thread's interrupted status to use higher up on the call stack
Thread.currentThread().interrupt();
// Continue waiting for response (unless you specifically intend to use the interrupt to cancel your request)
}
}
// Do something with response, i.e.
new SignUpResponseListener().onResponse(response);
} catch (ExecutionException e) {
// Do something with error, i.e.
new MyErrorListener().onErrorResponse(new VolleyError(e));
} catch (TimeoutException e) {
// Do something with timeout, i.e.
new MyErrorListener().onErrorResponse(new VolleyError(e));
}
If you want to do something exactly after the Volley request, use a callback listener onSuccess (SignUpResponseListener in that case) and put the code there. This is the best practice.
I couldn't get RequestFuture working so I just used a callback listener like Makibo suggested. I've no idea why he was downvoted, this is probably the best solution for the original common problem of different Volley requests that all depends on an initial Login or something. In this example, I want to upload a photo but first I've to check if user is logged in or not. If not, I've to login and then wait for success before uploading the photo. If already logged in, just go straight to uploading the photo.
Here's my sample code:
// interface we'll use for listener
public interface OnLoginListener {
public void onLogin();
}
public void uploadPhoto(final String username, final String password, final String photo_location) {
// first setup callback listener that will be called if/when user is logged in
OnLoginListener onLoginListener=new OnLoginListener() {
#Override
public void onLogin() {
uploadPhotoLoggedIn(photo_location);
}
};
// simplistic already logged in check for this example, just checking if username is null
if (loggedInUsername==null) {
// if it null, login and pass listener
httpLogin(username, password, onLoginListener);
} else {
// if not null, already logged in so just call listener method
onLoginListener.onLogin();
}
}
public void httpLogin(String username, String password, final OnLoginListener onLoginListener) {
StringRequest loginRequest = new StringRequest(Request.Method.POST, "https://www.example.com/login.php", new Response.Listener<String>() {
#Override
public void onResponse(String txtResponse) {
Log.d("STACKOVERFLOW",txtResponse);
// call method of listener after login is successful. so uploadPhotoLoggedIn will be called now
onLoginListener.onLogin();
} },
new Response.ErrorListener()
{
#Override
public void onErrorResponse(VolleyError error) {
// TODO Auto-generated method stub
Log.d("VOLLEYERROR","error => "+error.toString());
}
}
) {
};
// Just getting the Volley request queue from my application class (GetApplicatio.java), and adding request
GetApplication.getRequestQueue().add(loginRequest);
}
I want to add something to Gabriel's answer. While RequestFuture blocks the thread from which it is called and it serves your purpose, the network request itself is not carried out in that thread. Instead, it is carried out on a background thread.
From what I understand after going through the library, requests in the RequestQueue are dispatched in its start() method:
public void start() {
....
mCacheDispatcher = new CacheDispatcher(...);
mCacheDispatcher.start();
....
NetworkDispatcher networkDispatcher = new NetworkDispatcher(...);
networkDispatcher.start();
....
}
Now both CacheDispatcher and NetworkDispatcher classes extend thread. So effectively a new worker thread is spawned for dequeuing the request queue and the response is returned to the success and error listeners implemented internally by RequestFuture.
So I see no point in making a separate blocking thread to use RequestFuture. Instead as Makibo mentioned in his answer - " use a callback listener onSuccess (SignUpResponseListener in that case) and put the code there."
For me without the timeout in get() it always blocked, and with a timeout it always timed out.
Turns out it not must be on the main UI thread, therefore create a thread to make the async requests:
Thread t = new Thread(() -> {
doAsyncRequestHere();
});
t.start();