I'm using Volley however I'm having some problems with the JSON parsed data most likely because volley doesn't implement something like AsyncTask's onPostExecute() and I'm getting some duplicated data on wrong list items.
Then I came across this: https://github.com/yakivmospan/volley-request-manager#custom-listener-implementation-
Has anyone use it? How can I add it to my current Volley code?
More details about my problem here Volley not sending correct data. How to implement an alternative to onPostExecute()?
UPDATE
As requested, some code. Here's a button that calls a method on another class that uses Volley to request some raw JSON data (NovaJSON) and then send the JSON to a parser class (NovaParser):
info.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
String instanceDetail = NovaJSON.shared().receiveDetail(getId());
Dialog dialog = new Dialog(v.getContext());
dialog.setContentView(R.layout.instances_info);
TextView image = (TextView) dialog.findViewById(R.id.imageInstance);
TextView flavor = (TextView) dialog.findViewById(R.id.flavorInstance);
dialog.setTitle(name.getText() + " Details");
if (instanceDetail != null) {
image.setText(" \u2022 image : " + NovaParser.shared().parseImages(instanceDetail));
flavor.setText(" \u2022 flavor : " + NovaParser.shared().parseFlavor(instanceDetail));
}
dialog.show();
}
});
This is the method that does the Volley request on the NovaJSON class:
public void getJSONdetail() {
final String authToken = getAuth();
String novaURL = getNova();
novaURL = novaURL+"/servers/"+id;
JsonObjectRequest getRequest = new JsonObjectRequest(Request.Method.GET, novaURL, null,
new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
Log.d("Nova on Response", response.toString());
setNovaJSONdetail(response.toString());
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
VolleyLog.d("Nova on Error", "Error: " + error.getMessage());
setNovaJSONdetail(error.toString());
}
}
) {
public Map<String, String> getHeaders() throws AuthFailureError {
Map<String, String> params = new HashMap<String, String>();
params.put("X-Auth-Token", authToken);
params.put("User-Agent", "stackerz");
params.put("Accept", "application/json");
params.put("Content-Type", "application/json; charset=utf-8");
return params;
}
};
queue = VolleySingleton.getInstance(this).getRequestQueue();
queue.add(getRequest);
}
It then sends the JSON from the server as a string to be parsed using the following methods:
public static String parseImages(String imagesDetail){
ArrayList<HashMap<String, String>> imagesList = NovaParser.shared().getImagesList();
String temp = null;
JSONObject novaDetail = null;
try {
novaDetail = new JSONObject(imagesDetail);
JSONObject server = novaDetail.getJSONObject("server");
JSONObject image = server.getJSONObject("image");
if (imagesList !=null){
temp = image.getString("id");
for (Map<String,String> map : imagesList) {
if (map.containsValue(temp)) {
temp = map.get(NAME);
}
}
}
} catch (JSONException e) {
e.printStackTrace();
}
return temp;
}
public static String parseFlavor(String instanceDetail){
ArrayList<HashMap<String, String>> flavorList = NovaParser.shared().getFlavorList();
String temp = null;
JSONObject novaDetail = null;
try {
novaDetail = new JSONObject(instanceDetail);
JSONObject server = novaDetail.getJSONObject("server");
JSONObject flavor = server.getJSONObject("flavor");
if (flavorList !=null){
temp = flavor.getString("id");
for (Map<String,String> map : flavorList) {
if (map.containsValue(temp)) {
temp = map.get(NAME);
}
}
}
} catch (JSONException e) {
e.printStackTrace();
}
return temp;
}
When I press the button once the dialog is displayed with empty values. When I press it the second time I get the correct parsed data. Basically first time I click the button the instanceDetail string is null because Volley didn't finish doing its thing then I click the 2nd time it loads the values accordingly because it finally finished the 1st request.
I understand Volley is asynchronous, the requests happen in parallel and the responses sometimes are not immediate however I need some sort of progress bar or spinning wheel to give the user some feedback that the app is waiting for data. It could be done with AsyncTask however it doesn't seem to be possible with Volley.
I think your problem is not because of Volley.
Check the parameters you send and receive.
However if you need onPostExcecute you have Volley's callback:
Response.Listener<JSONObject> and Response.ErrorListener() which are called after the request.
About Volley request manager just switch all your volley calls with appropriate Volley request manager calls
I solved my problem by dumping Volley altogether and moving to Retrofit. I setup all the calls to be sync/blocking, worked out the exceptions/errors using try/catches and setup a short timeout on the OkHTTP client. Now it's working as I wanted.
Related
I know this question has been asked a few times and i have tried all the solutions however, nothing seems to work. My method:
public static LocationGeoData getLocationGeoData(Location location){
RequestQueue requestQueue = Volley.newRequestQueue(MyApplication.getAppContext());
Date dateNow = new Date();
SimpleDateFormat fmt = new SimpleDateFormat("yyyy.MM.dd");
String url = "MYCORRECTURL";
Log.d("geoData", "In getGeoData " + url); // this is called and logs
JsonObjectRequest request = new JsonObjectRequest(Request.Method.GET, url,
null,
new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
Log.d("geoData", "inside the response");// this never gets called
try {
JSONObject jsonObject = response.getJSONObject("data");
for(int i = 0; i <jsonObject.length(); i++){
JSONObject heading = jsonObject;
if(heading.getString("field-value").equals("field-value")){
JSONObject totIntensity = heading.getJSONObject("total-intensity");
JSONObject declination = heading.getJSONObject("declination");
JSONObject inclination = heading.getJSONObject("inclination");
int totalIntensity = totIntensity.getInt("value");
double declinationValue = declination.getDouble("value");
double inclinationValue = inclination.getDouble("value");
locationGeoData = new LocationGeoData(totalIntensity, declinationValue, inclinationValue);
}
}
} catch (JSONException e) {
Log.d("geoData", "Error recorded");//never called
e.printStackTrace();
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Log.d("geoData", "Error recorded");// never called
}
});
requestQueue.add(request);
return locationGeoData;
}
The second Log message inside the response never gets called, i get no error messages, my url works and i can see the JsonOBject in the browser when tested, but the response is never called in my method. Can anyone advise me what i am doing wrong?
I want to parse a single url from my remote json file. I have a Button code in onCreate and I want to parse url from my json object to my DynamicButton.
private void parseJSON() {
String url = https://www.example.com/data.json
JsonObjectRequest request = new JsonObjectRequest(Request.Method.GET, url, null,
new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
try {
JSONArray jsonArray = response.getJSONArray("MyDynamicUrl");
for (int i = 0; i < jsonArray.length(); i++) {
JSONObject hit = jsonArray.getJSONObject(i);
String myDynamicLink = hit.getString("Link");
}
} catch (JSONException e) {
e.printStackTrace();
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
error.printStackTrace();
}
});
mRequestQueue.add(request);
}
I have this button in onCreate and Now I want to parse myDynamicLink to this button. I am getting Error "Can not resolve symbol 'MyDynamicLink' "
DLbtn = findViewById(R.id.DynamicLinkButton);
DLbtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
String url = myDynamicLink;
Intent i = new Intent(Intent.ACTION_VIEW);
i.setData(Uri.parse(url));
startActivity(i);
}
});
My json file structure
{
"MyDynamicUrl": [
{"Link":"https://www.myDynamicUrl.com"}
]}
You should define myDynamicLink as a field outside of the method and then set a value to it:
private String myDynamicLink;
private void parseJSON() {
...
myDynamicLink = hit.getString("Link");
...
}
Also note, that the request is made asynchronously (on another thread),
it means that your button may be already initialized and you can click it, but possibly you may still not receive a response.
In addition, you may start using a library for converting JSON objects to Java objects, such as Gson, it will let you much easily parse the JSON.
I need a little help :)
I'm using volley on my Android app and I wrote this codes.
public String validateCredentials(final String email, final String password) {
StringRequest strReq = new StringRequest(com.android.volley.Request.Method.POST,
LOGIN_URL, new com.android.volley.Response.Listener<String>() {
#Override
public void onResponse(String response) {
try {
JSONObject serverResponse = new JSONObject(response);
Log.d("Result: ", serverResponse.getString("result"));
responseServ = serverResponse.getString("result");
} catch (JSONException e) {
e.printStackTrace();
}
}
}, new com.android.volley.Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
}
}) {
#Override
protected Map<String, String> getParams() {
Map<String, String> params = new HashMap<String, String>();
params.put(email, password);
return params;
}
};
AppController.getInstance().addToRequestQueue(strReq);
return responseServ;
}
When I click the button Log.d("Result: ", serverResponse.getString("result")); this code is work but return responseServ; is not send any data on first click.
My button onClick code is
Toast.makeText(activity, authModel.validateCredentials(email, password), Toast.LENGTH_SHORT).show();
How do I solve this problem?
Thanks in advance
Volley is asynchronous aka you make the call, then later the callback is executed (Log.d() part). But you are also synchronously returning value which is empty the first time, and only the second time return values.
Have in mind that the second time it returns the first result.
What you have to do is do all your work in onResponse()
PS: As you want to keep MVP pattern you can- Define callback Interface and pass it to validateCredentials(final String email, final String password, final OnLoginComplete callback) and then in onResponse() callback.loginComplete()
I've tried with normal JSONArrayRequests and StringRequests and everything was fine untill now. I want to send an JSONArrayRequest with POST parameters to get some MySQL result in JSON format from the script. Unfortunately I get [] everytime in response. I have checked .php file and query with _GET method and the script worked perfectly returning desired rows in Json format.
I read here (https://stackoverflow.com/a/18052417/4959185) Volley Team have added JSONArrayRequest with _POST parameter to their class. However it does not work in my case. Could you please look what is wrong with that function:
private void getFavouriteRecipes(final String userUniqueId, final int offset) {
JsonArrayRequest favouriteRecipesReq = new JsonArrayRequest(Request.Method.POST,
AppConfig.URL_GETFAVOURITERECIPES, new Response.Listener<JSONArray>() {
#Override
public void onResponse(JSONArray response) {
Log.d("odpowiedz", "Odpowiedź ulubionych: " + response);
for (int i = 0; i < response.length(); i++) {
try {
JSONObject jObj = response.getJSONObject(i);
RecipeItem recipeItem = new RecipeItem();
recipeItem.setRecipeUniqueID(jObj.getString("unique_id"));
recipeItem.setRecipeTitle(jObj.getString("title"));
recipeItem.setRecipeImgThumbnailLink(jObj.getString(
"img_tumbnail_link"));
recipeItem.setRecipeAddAte(jObj.getString("add_date"));
recipeItem.setRecipeKitchenType(jObj.getString("kitchen_type"));
recipeItem.setRecipeMealType(jObj.getString("meal_type"));
recipeItem.setRecipeName(jObj.getString("name"));
recipeItem.setRecipeSurname(jObj.getString("surname"));
recipeItem.setRecipeLikeCount(jObj.getString("like_count"));
recipeFavouriteItems.add(recipeItem);
} catch (JSONException e) {
e.printStackTrace();
showSnackbarInfo("Błąd Json: " + e.getMessage(),
R.color.snackbar_error_msg);
}
}
recipeFavouriteItemsAdapter.notifyDataSetChanged();
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Log.e("odpowiedz", "Błąd pobierania ulubionych: " +
Integer.toString(error.networkResponse.statusCode));
showSnackbarInfo(Integer.toString(error.networkResponse.statusCode),
R.color.snackbar_error_msg);
}
}) {
#Override
protected Map<String, String> getParams() {
// Posting Parameters to Login URL
Map<String, String> params = new HashMap<>();
params.put("user_unique_id", userUniqueId);
params.put("offset", Integer.toString(offset));
Log.d(TAG, "wysylam parametry: " + userUniqueId + ", " + Integer.toString(offset));
return params;
}
};
// Adding Request to Request Queue
AppController.getInstance().addToRequestQueue(favouriteRecipesReq);
}
My PHP Script:
https://ideone.com/ZxYzHr
I have found another way to get JSONArrayResponse with sending parameters. I think that will help somebody.
U just write standard JSONArrayRequest liek this:
JsonArrayRequest favouriteRecipesReq = new JsonArrayRequest(prepareGetMethodUrl(),
new Response.Listener<JSONArray>() {
#Override
public void onResponse(JSONArray response) {
Log.d("odpowiedz", "Odpowiedź ulubionych: " + response.toString());
for (int i = 0; i < response.length(); i++) {
try {
JSONObject jObj = response.getJSONObject(i);
RecipeItem recipeItem = new RecipeItem();
recipeItem.setRecipeUniqueID(jObj.getString("unique_id"));
recipeItem.setRecipeTitle(jObj.getString("title"));
recipeItem.setRecipeImgThumbnailLink(jObj.getString(
"img_tumbnail_link"));
recipeItem.setRecipeAddAte(jObj.getString("add_date"));
recipeItem.setRecipeKitchenType(jObj.getString("kitchen_type"));
recipeItem.setRecipeMealType(jObj.getString("meal_type"));
recipeItem.setRecipeName(jObj.getString("name"));
recipeItem.setRecipeSurname(jObj.getString("surname"));
recipeItem.setRecipeLikeCount(jObj.getString("like_count"));
recipeFavouriteItems.add(recipeItem);
} catch (JSONException e) {
e.printStackTrace();
}
}
recipeFavouriteItemsAdapter.notifyDataSetChanged();
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Log.e("odpowiedz", "Błąd pobierania ulubionych: " +
Integer.toString(error.networkResponse.statusCode));
showSnackbarInfo(Integer.toString(error.networkResponse.statusCode),
R.color.snackbar_error_msg);
}
});
// Adding Request to Request Queue
AppController.getInstance().addToRequestQueue(favouriteRecipesReq);
Instead of standard URL to the PHP script I inserted function returning String called prepareGetMethodUrl().
Let's look inside it:
private String prepareGetMethodUrl() {
return AppConfig.URL_GETFAVOURITERECIPES + "?user_unique_id=" + userUniqueId + "&offset=" +
Integer.toString(offset);
}
As you can see it's very simple. I get standard AppConfig.URL_GETFAVOURITERECIPES which is static field in AppConfig class conatining direct link to my PHP script on my serwer f.e http://www.someserversite.com/my_api/gmy_php_script.php and combine it with parametres values I need to send to the script: user_unique_id and it's content userUniqueId and offset which content is offset parsed from int to String.
Inside my script I just call:
<?php
// some code
// Receiving The Post Params
$user_unique_id = $_GET['user_unique_id'];
$offset = $_GET['offset'];
echo $user_unique_id . "<br />";
echo $offset;
?>
This is regarding Weatherforecastapp using Volley..
How do we replace the following code with Volley?
Since we are new to android we are finding it difficult to implement Volley.
private class JSONWeatherTask extends AsyncTask<String, Void, Weather> {
#Override
protected Weather doInBackground(String... params) {
Weather weather = new Weather();
String data = ( (new WeatherHttpClient()).getWeatherData(params[0], params[1]));
try {
weather = JSONWeatherParser.getWeather(data);
System.out.println("Weather ["+weather+"]");
// Let's retrieve the icon
weather.iconData = ( (new WeatherHttpClient()).getImage(weather.currentCondition.getIcon()));
} catch (JSONException e) {
e.printStackTrace();
}
return weather;
}
#Override
protected void onPostExecute(Weather weather) {
super.onPostExecute(weather);
if (weather.iconData != null && weather.iconData.length > 0) {
Bitmap img = BitmapFactory.decodeByteArray(weather.iconData, 0, weather.iconData.length);
imgView.setImageBitmap(img);
}
cityText.setText(weather.location.getCity() + "," + weather.location.getCountry());
temp.setText("" + Math.round((weather.temperature.getTemp() - 275.15)));
condDescr.setText(weather.currentCondition.getCondition() + "(" + weather.currentCondition.getDescr() + ")");
}
}
The good news is that Volley is much easier to use than AsyncTask! :)
Volley has a a few types of requests that it can make. For your implementation, it looks like you are retrieving JSON. Volley has special request for JSONObject and JSONArray, so you would use whichever makes sense for you.
Here is a basic outline of how you would replace your code with Volley. Note that the onResponse is the callback (like onPostExecute in AsyncTask).
private class WeatherTask{
public void getWeatherData() {
// Create a single queue
RequestQueue queue = Volley.newRequestQueue(this);
// Define your request
JsonObjectRequest getRequest = new JsonObjectRequest(url,
new Response.Listener<JSONArray>() {
#Override
public void onResponse(JsonObject jsonObject) {
// Parse JSON here
}
}
}
, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError volleyError) {
// Show Error message
Log.e("Error Response: ", volleyError.toString());
}
}
);
// add it to the Request Queue
queue.add(getRequest);
}
}
Here is a great talk on Volley, to learn more about it: https://developers.google.com/events/io/sessions/325304728