I am working on an Android App, in which I need to make a lot of http queries. Since Android has a constrain to prevent program making http request on UI thread, I have to use a lot of Async methods to get response from the server. I am not a guru of using callbacks, there's a code design problem I am facing now:
Basically, I have a activity to display. I want the app to make a HttpRequest to server when the activity is created, so I can prepare the content of the activity based on the query response.
Since I am using the Google Volley library to make http requests, my code design is:
// in the Activity
OnCreate(Bundle b){
String response = RequestManager.makeRequest(args);
// other works based on the response in this activity.
}
// RequestManager Class
public static String makeRequest(args){
String url = getUrl();
// response callback
Response.Listener<String> responseListener = new Response.Listener<String>() {
#Override
public void onResponse(String response) {
// Don't know what to do here
}
};
// error callback
Response.ErrorListener errorListener = new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
// deal with errors
}
};
BuildRequestAndPushToQueue(url, responseListener, errorListener);
// No way to return the response!
}
I know my design is totally incorrect, because String response = RequestManager.makeRequest(args); is intent to wait for a blocking call, but the call is actually async.
My ultimate goal is to let the response String returned to some code in the activity, so it can use the activity context to do the rest works (like access to a imageview, etc). But I am not sure how to design the code flow to make this happen.
Related
I'm building a tracking app for exercise. As the gps updates every 1 second, the latitude and longitude is added to an array. At the end of the exercise, when you press save, below method is executed, sending all co-ordinates to a database. Because it's an asynchronous request, the co-ordinates don't get loaded into the database in correct order. How can I fix this so it will wait until each iteration of loop is complete or something like that. Thanks
/* Inserts the latitude and longitude points from the latitudeAndLongitudePoints ArrayList
into the latitudeandlongitudepoints table in db*/
private void insertLatitudeAndLongitudePoints(){
//iterates though array of co-ordinates, and adds to database
for(int loop=0; loop<latitudeAndLongitudePoints.size();loop++) {
final int index = loop;
StringRequest stringRequest = new StringRequest(Request.Method.POST,
"http://rrush01.lampt.eeecs.qub.ac.uk/insertLatitudeAndLongitudePoints.php",
new Response.Listener<String>() {
#Override
public void onResponse(String response) {
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Toast.makeText(getApplicationContext(), "Error: " + error.getMessage(), Toast.LENGTH_LONG).show();
}
}) {
#Override
protected Map<String, String> getParams() throws AuthFailureError {
Map<String, String> params = new HashMap<>();
params.put("Latitude", String.valueOf(latitudeAndLongitudePoints.get(index).latitude));
params.put("Longitude", String.valueOf(latitudeAndLongitudePoints.get(index).longitude));
params.put("User", AccountInfo.accountEmail);
return params;
}
};
RequestQueue queue = Volley.newRequestQueue(this);
queue.add(stringRequest);
}
}
Add dependency for Volley library in your build.gradle.
compile 'com.android.volley:volley:1.0.0'
Volley is a network library which makes fast and easier to make HTTP Request for android application. Using Volley library we don’t need to create AsyncTask to make multiple API calls in volley there is RequestQueue where we can enqueue each request. Here in this example, We used Google place autocomplete API. We are going to make synchronous HTTP Request using volley. To make synchronous HTTP request make a RequestFuture object. And make JsonObjectRequest pass this parameter RequestMethod, URL, pass object of type Json and pass RequestFuture instance.
RequestFuture<JSONObject> requestFuture=RequestFuture.newFuture();
final String mURL = "https://maps.googleapis.com/maps/api/place/
autocomplete/json?key="+KEY+"&input=" + input;
JsonObjectRequest request = new JsonObjectRequest(Request.Method.POST,
mURL,new JSONObject(),requestFuture,requestFuture);
MySingleton.getInstance(mContext).addToRequestQueue(request);
Add your request in RequestQueue. Now, whenever the user makes an HTTP request each request added to RequestQueue.To get response object from the requestFuture we call get() method. And we also set a timeout of let’s say 10 seconds so we don’t block the UI thread indefinitely in case our request times out.
try {
JSONObject object= requestFuture.get(10,TimeUnit.SECONDS);
} catch (InterruptedException e|ExecutionException e|TimeoutException e) {
e.printStackTrace();
}
Hope this helps you to understand how we can use volley used to make the synchronous HTTP request
I am trying to write a volley get a request to fetch me all the users of my API. I have successfully done this, however, my function has stopped working. If I run my code now and attempt to fetch all the users, it will just return an empty array. If I try to fetch them one more time without stopping the run, it will return to me all of the users.
This is my function:
public ArrayList<User> getAllUsers(final VolleyCallBack callBack) {
requestQueue = Volley.newRequestQueue(this);
JsonArrayRequest arrayRequest = new JsonArrayRequest(
Request.Method.GET,
"http://ecoproduce.eu/api/User",
null,
new Response.Listener<JSONArray>() {
#Override
public void onResponse(JSONArray response) {
Log.e("Rest response", response.toString());
Gson gson = new Gson();
User user = new User();
for (int i = 0; i < response.length(); i++) {
try {
user = gson.fromJson(response.getJSONObject(i).toString(), User.class);
} catch (JSONException e) {
e.printStackTrace();
}
allUsers.add(user);
}
callBack.onSuccess();
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Log.e("Error response", error.toString());
}
}
);
requestQueue.add(arrayRequest);
return allUsers;
}
This is my callback interface:
public interface VolleyCallBack {
ArrayList<User> onSuccess();
}
This is how I call the function:
ArrayList<User> currentUsers;
currentUsers = getAllUsers(new VolleyCallBack() {
#Override
public ArrayList<User> onSuccess() {
Log.e("Bla", "ALL DONE!!!");
return allUsers;
}
});
I don't know what the problem is, this was working perfectly like an hour ago but for some reason, it has stopped. This is my first attempt at writing RESTful requests and I can't really pinpoint the error of my code.
SOLUTION:
Problem solved by making the changes suggested by Reaz Murshed and moving the getAllUsers call to the body of onCreate insted of it being within the onClick() function.
The getAllUsers function is returning allUsers immediately after putting a get request in a background thread. That being said, you are getting the user list when the background thread for your API was not finished fetching your user list and hence you are getting an empty list for the first time.
However, during the second time, you are having it, because the first API request fetched the users successfully and updated your allUsers list. So, actually you are not getting the user list from the second time API request, you are getting it from the request that was made previously.
To tackle this problem, do not return the allUsers from your getAllUsers function immediately after sending the API request. Wait for the API request to fetch the data for you and you already have a setup for success callback, where you should be able to get your desired user data.
Hence, you need to implement your onSuccess method in your Activity or Fragment or wherever you are calling the getAllUsers method, and perform necessary actions with the list of users on receiving a successful callback.
I would suggest modifying your callback function a little so that you can actually pass on the information that you fetched from the server.
public interface VolleyCallBack {
void onSuccess(ArrayList<User>);
}
And in the onResponse function of Volley callback, pass the user list that was fetched as follows.
callBack.onSuccess(allUsers);
I hope that helps!
I am working with an API for the first time.
My need is that I need to form an URL with certain parameters out of which one parameter cannot be formed in the Main UI thread and has to be formed in the Background thread.
I am planning to use to volley library to post the GET request.
I am using the Needle library which helps in running background tasks. This is What I have tried till now.
Needle.onBackgroundThread().execute(new UiRelatedTask<String>() {
#Override
protected String doWork() {
return url = GetUrl();
}
#Override
protected void thenDoUiRelatedWork(String result1) {
Log.e("JSON", url);
final JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(Request.Method.GET, url, null,
new Response.Listener() {
#Override
public void onResponse(Object response) {
Log.e("JSON", response.toString());
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Log.e("JSON", error.toString());
}
});
RequestQueue queue = VolleyController.getInstance(Activity.this.getApplicationContext()).getRequestQueue();
queue.start();
queue.add(jsonObjectRequest);
}
});
But the Main problem is that After the GetUrl() method immediately the thenDoUiRelatedWork method is called hence the request is made using an Invalid URL as the URL will still be loading in the background and then when the loading of the URL is over I am logging the URL Which is right.
I cannot use an AysncTask as my app is already using three AsyncTaks and additionally two more could be activated by the user based on the feature he is using. And also the API request has to be done in various places (3-4 places) hence using an AsyncTask will not be suitable.
Can anyone help me to first form the URL fully in the Background and the use volley to post the GET request.
I want to post some data to a MySQL database. The server-side code will determine whether the data is valid and respond with that determination. Upon receiving this response, the application-side code must decide if it needs to try again or continue on. I have tried callbacks, RequestFuture, and spin-waiting, but it is starting to seem like this type of functionality is not possible with Volley (making decisions based on the server response). Has anyone else had success in implementing this type of functionality?
Main Thread
postCampaign(campaign);
if (//data was invalid) {
//do postCampaign(campaign) again
}
Main Thread
private void postCampaign(final Campaign campaign) {
campaign.setRoomCode("XXXXXXXX");
StringRequest request = new StringRequest(Request.Method.POST, url,
new Response.Listener<String>()
{
#Override
public void onResponse(String response) {
// I want to make a decision based on response
}
},
new Response.ErrorListener()
{
#Override
public void onErrorResponse(VolleyError error) {
...
}
}
) {
#Override
protected Map<String, String> getParams() {
Map<String, String> params = new HashMap<>();
// put data
return params;
}
};
NetworkController.getInstance(this).addToRequestQueue(request);
}
from server side, you need to grab the data send by your app. Then you need to check for validations like is the passed data empty/incorrect(incase of passwords check for it against the password in database if its the correct one or not).
In the server side validation of your data, if the data is successfully validated and updated in database then give a success response with proper response code or else if database updation is failed, then give an error response with proper error response code. You may pass these response as a json format to the client(or in whatever format you are comfortable parsing)
Now in the app side you may have a response callback method(available in retrofit/volley). From the response obtained in this callback check if it is a success response or failure response and make appropriate decisions for success/ failure cases
My question is simple. I have a simple fragment which is not a list but simply 5 TextViews. Once this fragment runs, it fires a volley request to an API endpoint where its response arrives after the onCreateView method has already finished. Normally, one would use adapter.notifyDataSetChanged() so that the new data could be shown even if it arrives after the normal fragment lifecycles have ended. The problem is that since my fragment is not a list, it does not have an adapter. How could I use notifyDataSetChanged() in the absence of an adapter? What solution should I be looking for to solve this problem? Any feedback would be greatly appreciated.
Assuming you have a field of
private TextView textView1;
And somewhere you have a StringRequest for Volley, then in the onResponse for the Response.Listener, just set the text of the TextView.
public void onResponse(String response) {
textView1.setText(response);
}
If you have a seprate class that does your Volley requests, then you should add a parameter for Response.Listener to your method.
For example
MyServer.doLogin("http://myurl.com/login", "username:password", new Response<String>.Listener() {
public void onResponse(String response) {
// Do something here
}
};
Where MyServer.doLogin could be something like this (note: this is an example, it may not compile)
public void doLogin(String url, String cred, Response<String>.Listener callback) {
StringRequest request = new StringRequest(
url,
Request.GET,
callback,
new Response.ErrorListener() { ... }
);
addToRequestQueue(request);
}
You may try to reset the data collection and use Invalidate()to refresh the view ,try to mix these operations in a method and call them when you need.