Asynchronous HTTP Requests in Android Using Volley

*Using Request Queue*

All requests in Volley are placed in a queue first and then processed, here is how you will be creating a request queue:

RequestQueue

RequestQueue mRequestQueue = Volley.newRequestQueue(this); // ‘this’ is Context
Ideally you should have one centralized place for your Queue, and the best place to initialize queue is in your Application class. Here is how this can be done:

ApplicationController.java

public class ApplicationController extends Application {

    /**
     * Log or request TAG
     */
    public static final String TAG = "VolleyPatterns";

    /**
     * Global request queue for Volley
     */
    private RequestQueue mRequestQueue;

    /**
     * A singleton instance of the application class for easy access in other places
     */
    private static ApplicationController sInstance;

    @Override
    public void onCreate() {
        super.onCreate();

        // initialize the singleton
        sInstance = this;
    }

    /**
     * @return ApplicationController singleton instance
     */
    public static synchronized ApplicationController getInstance() {
        return sInstance;
    }

    /**
     * @return The Volley Request queue, the queue will be created if it is null
     */
    public RequestQueue getRequestQueue() {
        // lazy initialize the request queue, the queue instance will be
        // created when it is accessed for the first time
        if (mRequestQueue == null) {
            mRequestQueue = Volley.newRequestQueue(getApplicationContext());
        }

        return mRequestQueue;
    }

    /**
     * Adds the specified request to the global queue, if tag is specified
     * then it is used else Default TAG is used.
     * 
     * @param req
     * @param tag
     */
    public <T> void addToRequestQueue(Request<T> req, String tag) {
        // set the default tag if tag is empty
        req.setTag(TextUtils.isEmpty(tag) ? TAG : tag);

        VolleyLog.d("Adding request to queue: %s", req.getUrl());

        getRequestQueue().add(req);
    }

    /**
     * Adds the specified request to the global queue using the Default TAG.
     * 
     * @param req
     * @param tag
     */
    public <T> void addToRequestQueue(Request<T> req) {
        // set the default tag if tag is empty
        req.setTag(TAG);

        getRequestQueue().add(req);
    }

    /**
     * Cancels all pending requests by the specified TAG, it is important
     * to specify a TAG so that the pending/ongoing requests can be cancelled.
     * 
     * @param tag
     */
    public void cancelPendingRequests(Object tag) {
        if (mRequestQueue != null) {
            mRequestQueue.cancelAll(tag);
        }
    }
}

Making asynchronous HTTP requests

Volley provides the following utility classes which you can use to make asynchronous HTTP requests:

JsonObjectRequest — To send and receive JSON Object from the Server
JsonArrayRequest — To receive JSON Array from the Server
StringRequest — To retrieve response body as String (ideally if you intend to parse the response by yourself)
Note: To send parameters in request body you need to override either getParams() or getBody() method of the request classes (as required) described below.

JsonObjectRequest

This class can be used to send and receive JSON object. An overloaded constructor of this class allows to set appropriate request method (DELETE, GET, POST and PUT). This is the class which you should be using frequently if you are working with a RESTful backend. The following examples show how to make GET and POST requests.

Using HTTP GET method:

*JsonObjectRequest*



final String URL = "/volley/resource/12";
// pass second argument as "null" for GET requests
JsonObjectRequest req = new JsonObjectRequest(URL, null,
       new Response.Listener<JSONObject>() {
           @Override
           public void onResponse(JSONObject response) {
               try {
                   VolleyLog.v("Response:%n %s", response.toString(4));
               } catch (JSONException e) {
                   e.printStackTrace();
               }
           }
       }, new Response.ErrorListener() {
           @Override
           public void onErrorResponse(VolleyError error) {
               VolleyLog.e("Error: ", error.getMessage());
           }
       });

// add the request object to the queue to be executed
ApplicationController.getInstance().addToRequestQueue(req);

Using HTTP POST method:

*JsonObjectRequest*

final String URL = "/volley/resource/12";
// Post params to be sent to the server
HashMap<String, String> params = new HashMap<String, String>();
params.put("token", "AbCdEfGh123456");

JsonObjectRequest req = new JsonObjectRequest(URL, new JSONObject(params),
       new Response.Listener<JSONObject>() {
           @Override
           public void onResponse(JSONObject response) {
               try {
                   VolleyLog.v("Response:%n %s", response.toString(4));
               } catch (JSONException e) {
                   e.printStackTrace();
               }
           }
       }, new Response.ErrorListener() {
           @Override
           public void onErrorResponse(VolleyError error) {
               VolleyLog.e("Error: ", error.getMessage());
           }
       });

// add the request object to the queue to be executed
ApplicationController.getInstance().addToRequestQueue(req);


*JsonArrayRequest*

This class can be used to retrieve JSON array but not JSON object and only HTTP GET is supported as of now. As it supports only GET, so if you are to specify some querystring parameters then append those in the URL itself. The constructor does not accept request parameteJsonArrayRequest

final String URL = "/volley/resource/all?count=20";
JsonArrayRequest req = new JsonArrayRequest(URL, new Response.Listener<JSONArray> () {
    @Override
    public void onResponse(JSONArray response) {
        try {
            VolleyLog.v("Response:%n %s", response.toString(4));
        } catch (JSONException e) {
            e.printStackTrace();
        }
    }
}, new Response.ErrorListener() {
    @Override
    public void onErrorResponse(VolleyError error) {
        VolleyLog.e("Error: ", error.getMessage());
    }
});

// add the request object to the queue to be executed
ApplicationController.getInstance().addToRequestQueue(req);

*SringRequest*

This class can be used to retrieve the response from server as String, ideally you should use this class when you intend to parse the response by yourself, e.g. if it is XML. It also provides overloaded constructors to further customize your request.


final String URL = "/volley/resource/recent.xml";
StringRequest req = new StringRequest(URL, new Response.Listener<String>() {
    @Override
    public void onResponse(String response) {
        VolleyLog.v("Response:%n %s", response);
    }
}, new Response.ErrorListener() {
    @Override
    public void onErrorResponse(VolleyError error) {
        VolleyLog.e("Error: ", error.getMessage());
    }
    add the request object to the queue to be executed
ApplicationController.getInstance().addToRequestQueue(req);
});

Setting a priority to the request is possible, this may be desirable in some cases where you want to move certain requests higher up in the order. The requests are processed from higher priorities to lower priorities, in FIFO order. To set a priority you need to override the getPriority() method of the request class. The current available priorities are – Priority.LOW, Priority.NORMAL, Priority.HIGH and Priority.IMMEDIATE.

Cancelling requests

Volley provides powerful APIs to cancel pending or ongoing requests, one reason when you need to do this is if user rotates his device while a request is ongoing you need to cancel that because the Activity is going be restarted. The easiest way to cancel a request is to call the cancelAll(tag) method of the request queue, this will only work if you have set a tag on the request object before adding it to the queue. The ability to tag requests allows you to cancel all pending requests for that tag in one method call.

Adding a request to the queue using a tag:

request.setTag("My Tag");

As per the ApplicationController class shown above, this is how you’ll add the request to the queue:

ApplicationController.getInstance().addToRequestQueue(request, "My Tag");

Cancelling all requests with the specified tag:

mRequestQueue.cancelAll("My Tag");

As per the ApplicationController class shown above, this is how you’ll cancel the requests:

ApplicationController.getInstance().cancelPendingRequests("My Tag");

Retrying failed requests and customizing request Timeout

There is no direct way to specify request timeout value in Volley, but there is a workaround, you need to set a RetryPolicy on the request object. The DefaultRetryPolicy class takes an argument called initialTimeout, this can be used to specify a request timeout, make sure the maximum retry count is 1 so that volley does not retry the request after the timeout has been exceeded.

Setting Request Timeout

request.setRetryPolicy(new DefaultRetryPolicy(20 * 1000, 1, 1.0f));

If you want to retry failed requests (due to timeout) you can specify that too using the code above, just increase the retry count. Note the last argument, it allows you to specify a backoff multiplier which can be used to implement “exponential backoff” that some RESTful services recommend.

Setting Request Headers (HTTP headers)

Sometimes it is necessary to add extra headers to the HTTP requests, one common case is to add an “Authorization” header for HTTP Basic Auth. Volley Request class provides a method called getHeaders() which you need to override to add your custom headers if necessary.

Adding custom headers:

JsonObjectRequest req = new JsonObjectRequest(URL, new JSONObject(params),
           new Response.Listener<JSONObject>() {
               @Override
               public void onResponse(JSONObject response) {
                   // handle response
               }
           }, new Response.ErrorListener() {
               @Override
               public void onErrorResponse(VolleyError error) {
                   // handle error                        
               }
           }) {

       @Override
       public Map<String, String> getHeaders() throws AuthFailureError {
           HashMap<String, String> headers = new HashMap<String, String>();
           headers.put("CUSTOM_HEADER", "Yahoo");
           headers.put("ANOTHER_CUSTOM_HEADER", "Google");
           return headers;
       }
   };

*Using cookies*

There is no direct API through which you can set cookies in Volley. This makes sense because the core philosophy of Volley is to provide clean APIs to write RESTful HTTP requests, these days most of the RESTful API providers prefer authentication tokens instead of cookies. Using cookies in Volley is a bit more involed process and not very straight forward.

Here is a modified version of the getRequestQueue() method of our ApplicationController class shown above, also contains the rough code required to set a cookie:

// http client instance
private DefaultHttpClient mHttpClient;
public RequestQueue getRequestQueue() {
    // lazy initialize the request queue, the queue instance will be
    // created when it is accessed for the first time
    if (mRequestQueue == null) {
        // Create an instance of the Http client. 
        // We need this in order to access the cookie store
        mHttpClient = new DefaultHttpClient();
        // create the request queue
        mRequestQueue = Volley.newRequestQueue(this, new HttpClientStack(mHttpClient));
    }
    return mRequestQueue;
}

/**
 * Method to set a cookie
 */
public void setCookie() {
    CookieStore cs = mHttpClient.getCookieStore();
    // create a cookie
    cs.addCookie(new BasicClientCookie2("cookie", "spooky"));
}


// add the cookie before adding the request to the queue
setCookie();

// add the request to the queue
mRequestQueue.add(request);

Error Handling

As you have seen in the above code examples when you create a request object in Volley you need to specify an error listener, Volley invokes the onErrorResponse callback method of that listener passing an instance of the VolleyError object when there is an error while performing the request.

The following is the list of exceptions in Volley:

AuthFailureError — If you are trying to do Http Basic authentication then this error is most likely to come.

NetworkError — Socket disconnection, server down, DNS issues might result in this error.

NoConnectionError — Similar to NetworkError, but fires when device does not have internet connection, your error handling logic can club NetworkError and NoConnectionError together and treat them similarly.

ParseError — While using JsonObjectRequest or JsonArrayRequest if the received JSON is malformed then this exception will be generated. If you get this error then it is a problem that should be fixed instead of being handled.

ServerError — The server responded with an error, most likely with 4xx or 5xx HTTP status codes.

TimeoutError — Socket timeout, either server is too busy to handle the request or there is some network latency issue. By default Volley times out the request after 2.5 seconds, use a RetryPolicy if you are consistently getting this error.

You can use a simple helper like the following to display appropriate message when one of these exceptions occurs:

VolleyErrorHelper.java

public class VolleyErrorHelper {
     /**
     * Returns appropriate message which is to be displayed to the user 
     * against the specified error object.
     * 
     * @param error
     * @param context
     * @return
     */
  public static String getMessage(Object error, Context context) {
      if (error instanceof TimeoutError) {
          return context.getResources().getString(R.string.generic_server_down);
      }
      else if (isServerProblem(error)) {
          return handleServerError(error, context);
      }
      else if (isNetworkProblem(error)) {
          return context.getResources().getString(R.string.no_internet);
      }
      return context.getResources().getString(R.string.generic_error);
  }

  /**
  * Determines whether the error is related to network
  * @param error
  * @return
  */
  private static boolean isNetworkProblem(Object error) {
      return (error instanceof NetworkError) || (error instanceof NoConnectionError);
  }
  /**
  * Determines whether the error is related to server
  * @param error
  * @return
  */
  private static boolean isServerProblem(Object error) {
      return (error instanceof ServerError) || (error instanceof AuthFailureError);
  }
  /**
  * Handles the server error, tries to determine whether to show a stock message or to 
  * show a message retrieved from the server.
  * 
  * @param err
  * @param context
  * @return
  */
  private static String handleServerError(Object err, Context context) {
      VolleyError error = (VolleyError) err;

      NetworkResponse response = error.networkResponse;

      if (response != null) {
          switch (response.statusCode) {
            case 404:
            case 422:
            case 401:
                try {
                    // server might return error like this { "error": "Some error occured" }
                    // Use "Gson" to parse the result
                    HashMap<String, String> result = new Gson().fromJson(new String(response.data),
                            new TypeToken<Map<String, String>>() {
                            }.getType());

                    if (result != null && result.containsKey("error")) {
                        return result.get("error");
                    }

                } catch (Exception e) {
                    e.printStackTrace();
                }
                // invalid request
                return error.getMessage();

            default:
                return context.getResources().getString(R.string.generic_server_down);
            }
      }
        return context.getResources().getString(R.string.generic_error);
  }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值