Varun Tewari Varun Tewari - 3 months ago 17
Android Question

Volley Request Sets the previous value to TextView

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);
}
});

Answer

Here is what happens when your function String getName() is called:

  1. 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.
  2. The JsonObjectRequest is added to the request queue, to be executed when a thread from the thread pool is available.
  3. 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).
  4. 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).