I am trying to understand how volley is used in android, So in my project I am querying a single name from http://uinames.com/api/?amount=1
In the app I have a button that is calling the function to make the request and then set it to the TextView. Also for debug Purposes I am logging the response from the url. On the button click the response is correctly seen in the logcat but the text is not set to the TextView until the next time the button is clicked.
Here is the code for the JsonObjectRequest
public String getName() {
JsonObjectRequest objectRequest = new JsonObjectRequest(Request.Method.GET,
URL, null,
new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
try {
full_name = response.getString(name) + " " + response.getString(last);
Log.d(TAG, full_name);
} catch (JSONException e) {
e.printStackTrace();
}
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
}
});
AppController.getInstance().addToRequestQueue(objectRequest, "");
return full_name;
}
Following is the Code from the MainActivity that calls the method getName()
mButton = (Button) findViewById(R.id.btn);
mTextView = (TextView) findViewById(R.id.text);
final RequestClass newReq = new RequestClass();
mButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
name = newReq.getName();
mTextView.setText(name);
}
});
Here is what happens when your function String getName() is called:
A JsonObjectRequest is constructed, and a response and error listeners are passed to the request. Note that the listeners are not executed until their corresponding events happen.
The JsonObjectRequest is added to the request queue, to be executed when a thread from the thread pool is available.
Until now the full_name variable has not been touched, and you return the last value that was assigned to it (that's why when you click the button again, you get a name in your text view).
Your request gets a response and the listener is called assigning a new value to full_name after your function has returned. that is the value that you get on the next call to getName(). . .
In general, a function like getName can not perform a network operation and return its result immediately. It either needs to block until it has the response from the server (and this is prohibited in android). Or, it can return without the result being fetched yet, and have a callback function (or a listener) that is called with the result when it is available (and this is the the general approach in the Volley library).
That means that you have to setText in your response listener. I have noticed that you have a separated class for your network requests RequestClass, to keep your design as it is, one solution would be to pass the listeners to getName() function (and all other functions that do network stuff in this class), it may look something like this:
public void getName(Response.Listener<JSONObject> responseListener,
Response.ErrorListener errorListener) {
JsonObjectRequest objectRequest = new JsonObjectRequest(Request.Method.GET,
URL, null, responseListener, errorListener);
AppController.getInstance().addToRequestQueue(objectRequest, "");
}
And in your activity, you have to pass listeners like this:
mButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
name = newReq.getName(
new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
try {
String full_name = response.getString("name") + " " + response.getString("last");
mTextView.setText(full_name);
Log.d(TAG, full_name);
} catch (JSONException e) {
e.printStackTrace();
}
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
}
});
}
});
As a better design, you may want to avoid passing Volley listeners from the activity (which does not have to know anything about volley), so you may want to define your own listener interface, override and pass that from the activity to your networking class (where you instantiate your Response.Listener, Response.ErrorListener and override them to call functions in your listener).
Volley library is deprecated so don't use Volley library.
The best alternative for Volley is RetroFit library.
Try like this
public void getName() {
JsonObjectRequest objectRequest = new JsonObjectRequest(Request.Method.GET,
URL, null,
new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
try {
full_name = response.getString("name") + " " + response.getString("last");
runOnUiThread(new Runnable() {
#Override
public void run() {
//Your code to run in GUI thread here
mTextView.setText(full_name);
}
});
Log.d(TAG, full_name);
} catch (JSONException e) {
e.printStackTrace();
}
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
}
});
AppController.getInstance().addToRequestQueue(objectRequest, "");
}
mButton = (Button) findViewById(R.id.btn);
mTextView = (TextView) findViewById(R.id.text);
mButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
getName();
}
});
Related
I am working on an Android app.
This is one function inside a fragment:
private void guardar_paciente() {
String tag_string_req = "req_login";
StringRequest strReq = new StringRequest(Request.Method.POST,
URL_CHECK, new Response.Listener<String>() {
#Override
public void onResponse(String response) {
try {
JSONObject jObj = new JSONObject(response);
boolean error = jObj.getBoolean("error");
String id_paciente = jObj.getString("id");
String id_nhc = jObj.getString("nhc");
if (!error) {
editor2.putString("id_paciente", id_paciente);
editor2.putString("nhc", id_nhc);
editor2.apply();
} else {
// Error in login. Get the error message
// String errorMsg = jObj.getString("error_msg");
}
} catch (JSONException e) {
// JSON error
e.printStackTrace();
// Toast.makeText(getActivity(), "Json error: " + e.getMessage(), Toast.LENGTH_LONG).show();
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Toast.makeText(getActivity(),
error.getMessage(), Toast.LENGTH_LONG).show();
}
}) {
#Override
protected Map<String, String> getParams() {
// Posting parameters to login url
Map<String, String> params = new HashMap<String, String>();
params.put("nombre_completo", nombre_completo);
params.put("apellidos", apellidos);
params.put("tel1", tel1);
params.put("tel2", tel2);
params.put("email", email);
params.put("profesion", profesion);
params.put("sexo", sexo);
params.put("fecha_nacimiento", fecha_nacimiento);
params.put("edad", edad);
params.put("peso", peso);
params.put("talla", talla);
params.put("IMC", IMC);
params.put("consentimiento", "1");
params.put("clinica_paciente", clinica_actual);
params.put("imagen_paciente", imagen_paciente);
params.put("firma_paciente", numero+".JPG");
params.put("DNI", DNI);
params.put("direccion", direccion);
params.put("raza", raza);
return params;
}
};
// Adding request to request queue
AppController.getInstance().addToRequestQueue(strReq, tag_string_req);
}
What I need is to execute another function, abrirPaciente(), just after guardar_paciente() has finished all its methods and it is safe to change the UI.
I have tried calling abrirPaciente() just after editor2.apply();, but the app crashes.
Making use of callbacks :
public class foo {
interface ExampleInterface {
void finishedServiceCallNoErrors();
void finishedServiceCallWithError(String error);
}
void guardar_paciente(ExampleInterface exampleInterface) {
...
#Override
public void onResponse(String response) {
....
//there was no error
exampleInterface.finishedServiceCallNoErrors();
//there was an error
exampleInterface.finishedServiceCallWithError("your error message here");
....
}
...
}
}
and an example of how you'd make use of this :
public class bar implements foo.ExampleInterface {
//simple example of how you'd use this
private void callingIt() {
new foo().guardar_paciente(this); //here, we can pass `this` because our class is implementing the interface
}
//these now get returned to the class making use of the service call, so you can easily handle things here, instead of worrying about the logic in your service call response
#Override
public void finishedServiceCallNoErrors() {
//TODO("Handle the response with no error");
//your call completed and everything was fine, now do something else
}
#Override
public void finishedServiceCallWithError(String error) {
// TODO("Handle the response with an error")
// there was an error, handle it here
}
}
I'm not sure if this callback pattern will be safe to use if it's being triggered from a background thread, so for that you'd need to switch threads inside the callbacks, so inside finishedServiceCallNoErrors and inside finishedServiceCallWithError you'd potentially need like a Runnable, so you can make use of the main thread, or inside the onResponse of the service call, before triggering the callbacks, you could also change there to the main thread. You can find help with something like that here
I am working with volley library in my android app development.
I have a base url and I need to append some value at the end of the url,click here,
So, this value "ZGxb87HuJK" keeps changing dynamically in my program and need to append this value at the end of url. How to add this in params?
Use this way.
StringRequest strreq = new StringRequest(Request.Method.GET,
"https://sample.com/testing/" + Hear Your dynamic value,
new Response.Listener<String>() {
#Override
public void onResponse(String Response) {
// get response
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError e) {
e.printStackTrace();
}
});
Volley.getInstance(this).addToRequestQueue(strreq);
String URL = "https://sample.com/testing/" + "dynamic value e.g ZGxb87HuJK";
JsonObjectRequest jsonObjReq = new JsonObjectRequest(Request.Method.GET,
URL, null,
new Response.Listener() {
#Override
public void onResponse(JSONObject response) {
//Success Callback
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
//Failure Callback
}
});
// Adding the request to the queue along with a unique string tag
MyApplication.getInstance().addToRequestQueue(jsonObjectReq, "getRequest");
Change your code like this
So I've run into a problem in my code, where I need to get a JSONString from my server with volley. And then I have to parse the String into a JSONObject and then continue doing stuff with that.
My problem here is, that Volley gives the response too late, meaning my string that I want to parse is always empty because its not initialised yet.
RequestFuture<String> future = RequestFuture.newFuture();
StringRequest stringRequest = new StringRequest(Request.Method.GET, searchURLBuilder, future, future);
// new Response.Listener<String>() {
// #Override
// public void onResponse(String response) {
// writeToSharedResponse(response_for_search, response);
// }
// },
// new Response.ErrorListener() {
// #Override
// public void onErrorResponse(VolleyError error) {
// Log.d("Error!!:" + error.getMessage(), "");
// }
// });
requestQueue.add(stringRequest);
String response = "";
try {
response = future.get(60, TimeUnit.SECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
String responseString = m.getString(response_for_search, new String());
pieceDTOList = getPiecesDTOFromJSON(responseString);
Here is a snippet of my code. As you can see I already tried to make a "future" call to block and wait for the answer, but it just times out every time. The commented out bit, is the part I actually wanted to use from the beginning, but that returns the response to late. Since its asynchronous and accesses the server w/e it wants.
writeToSharedResponse just writes the answer into a sharedPreferences variable.
private SharedPreferences m;
private SharedPreferences.Editor editor;
private RequestQueue requestQueue;
public DbParser(Context c) {
m = PreferenceManager.getDefaultSharedPreferences(c);
editor = m.edit();
requestQueue = Volley.newRequestQueue(c);
}
My Question here is: Is there an easy way I can "wait" for the answer from volley so I can continue to work with the response that I get?
Edit 1:
I now added an interface and changed the code around to this:
getString(new VolleyCallback() {
#Override
public void onSuccess(String result) {
getPiecesDTOFromJSON(result);
}
}, searchURLBuilder);
return globalPieceDTOList;
}
private void getString(final VolleyCallback callback, String searchUrl){
StringRequest stringRequest = new StringRequest(Request.Method.GET, searchUrl,
new Response.Listener<String>() {
#Override
public void onResponse(String response) {
callback.onSuccess(response);
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Log.d("Error!!:" + error.getMessage(), "");
}
});
requestQueue.add(stringRequest);
}
Since I cant get the values out of my inner classes, I did a nasty hack and created a global list for my DTO's. The problem now is that "return globalPieceDTOList" is always Null. And again - I would need to "wait" for the Volley response.
Volley requests are async, so when you try to return a value, the request is likely not done yet. If you want to return values from a Volley request use callback interfaces.
Example
public void getString(final VolleyCallback callback) {
StringRequest strReq = new StringRequest(Request.Method.GET, url, new Response.Listener<String>() {
#Override
public void onResponse(String response) {
// do some things here
callback.onSuccess(<PASS STRING RESULT HERE>);
}
});
}
public interface VolleyCallback{
void onSuccess(String result);
}
Example usage:
public void onResume(){
super.onResume();
getString(new VolleyCallback(){
#Override
public void onSuccess(String result){
//do stuff here with the result from the volley request
}
});
}
public void doMySearch(List<String> IDs) {
for (String Id : IDs) {
String url = "http://api.tvmaze.com/shows/" + Id + "?embed=nextepisode";
JsonObjectRequest jsObjRequest = new JsonObjectRequest
(Request.Method.GET, url, (String) null, new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
System.out.println("ONREQUEST ");
try {
String name = response.getString("name");
String airdate = response.getJSONObject("_embedded").getJSONObject("nextepisode").getString("airdate");
HashMap<String, String> episodesToDisplay = new HashMap<String, String>();
episodesToDisplay.put(name, airdate);
listOfEpisodes.add(episodesToDisplay);
} catch (JSONException e) {
e.printStackTrace();
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
// TODO Auto-generated method stub
}
});
System.out.println("BEFORE ADD TO REQUEST QUEUE ");
MySingleton.getInstance(this).addToRequestQueue(jsObjRequest);
System.out.println("AFTER ADD TO REQUEST QUEUE ");
}
System.out.println("AFTER FOR LOOP ");
}
See my prints in the code, the output I get with 4 items in my IDs list is:
BEFORE ADD TO REQUEST QUEUE
AFTER ADD TO REQUEST QUEUE
BEFORE ADD TO REQUEST QUEUE
AFTER ADD TO REQUEST QUEUE
BEFORE ADD TO REQUEST QUEUE
AFTER ADD TO REQUEST QUEUE
BEFORE ADD TO REQUEST QUEUE
AFTER ADD TO REQUEST QUEUE
AFTER FOR LOOP
ONREQUEST
ONREQUEST
ONREQUEST
ONREQUEST
The point is System.out.println("AFTER FOR LOOP "); is executed before onRequest and I want it to call a function in that place which would use a list populated inside onRequest. I thought onRequest would be executed after adding items to queue but it's not. Is there any way I can make it work in the order I expect? I cannot call funtion inside onRequest since I want it to use fully populated list and onRequest is called many times at once.
You have to create a callback to it.
Like this
public interface VolleyCallback {
void onSuccess(String string);
void onFailure(VolleyError error);
}
Then when you call your doMySearch function, you pass your callback, like this:
doMySearch(ids, new VolleyCallback() {
#Override
public void onSuccess(String response) {
Log.d("Success", response);
}
#Override
public void onFailure(VolleyError error) {
Log.e("Error", "ops thats an error!");
}
});
And finally, on your doMySearch function, where you override the onResponse and onErrorResponse listener you call your callback, like this:
public void doMySearch(List<String> IDs, final VolleyCallback callback) {
for (String Id : IDs) {
String url = "http://api.tvmaze.com/shows/" + Id + "?embed=nextepisode";
JsonObjectRequest jsObjRequest = new JsonObjectRequest
(Request.Method.GET, url, (String) null, new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
System.out.println("ONREQUEST ");
callback.onSuccess("ONREQUEST");
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
// TODO Auto-generated method stub
callback.onFailure(error);
}
});
System.out.println("BEFORE ADD TO REQUEST QUEUE ");
MySingleton.getInstance(this).addToRequestQueue(jsObjRequest);
System.out.println("AFTER ADD TO REQUEST QUEUE ");
}
System.out.println("AFTER FOR LOOP ");
}
Your code block is executed async, so you have to put your code inside the onResponse method.
i'm trying to use simple Volley JsonObjectRequest fro web service but i get this error:
error: reference to JsonObjectRequest is ambiguous, both constructor JsonObjectRequest(int,String,String,Listener<JSONObject>,ErrorListener) in JsonObjectRequest and constructor JsonObjectRequest(int,String,JSONObject,Listener<JSONObject>,ErrorListener) in JsonObjectRequest match
i'm install volley from Gradle and this is my simple code which i'm using in project.
public class Simple_JsonRequest extends Activity {
private TextView mTvResult;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.act__json_request);
mTvResult = (TextView) findViewById(R.id.tv_result);
Button btnJsonRequest = (Button) findViewById(R.id.btn_json_request);
btnJsonRequest.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
RequestQueue queue = MyVolley.getRequestQueue();
JsonObjectRequest myReq = new JsonObjectRequest(Request.Method.GET,
"http://echo.jsontest.com/key/value/one/two",
null,
createMyReqSuccessListener(),
createMyReqErrorListener());
queue.add(myReq);
}
});
}
private Response.Listener<JSONObject> createMyReqSuccessListener() {
return new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
try {
mTvResult.setText(response.getString("one"));
} catch (JSONException e) {
mTvResult.setText("Parse error");
}
}
};
}
private Response.ErrorListener createMyReqErrorListener() {
return new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
mTvResult.setText(error.getMessage());
}
};
}
}
How about you replace the null which is the 3rd argument, with something more specific and then try ?
Because JsonObjectRequest have two constructors,
one is
JsonObjectRequest(int,String,JSONObject,Listener<JSONObject>,ErrorListener)
and another is
JsonObjectRequest(int,String,String,Listener<JSONObject>,ErrorListener),so if you set the third parameter to null,the compiler won't know which constructor you want to use,so such an error occurred.
To solve your problem,you just can replace your third parameter to new JSONObject() or "".