In this tutorial, we’ll be discussing at length, an important networking library in our application, namely Android Volley. Android Volley library has been developed by Google. The first glimpses of it were seen in Google I/O . Let’s see what it has in store!
Android Volley
Networking is a key component in most of the Android Applications today. Using an AsyncTask might not be the best option when we have too many networking requests to handle. AsyncTask can’t prioritize parallel/multiple requests in an orderly manner. Besides they have a lot of boilerplate code. Here’s where Android Volley library comes as a handy tool. It has some powerful features and is a lot faster than AsyncTask.
Advantages of Android Volley Library
- Automatic scheduling of all network requests.
- Multiple concurrent network connections.
- Requests queuing and prioritization.
- Caching responses. It supports memory and disk-based caches.
- Cancelling one or more requests.
- Built-in support for String, Images, JSONObject and JSONArray Requests.
- Ability to create custom requests.
- Built-in NetworkImageView widget to load images from a URL easily.
- Support for retrying requests.
- Powerful debugging and tracing tools to catch errors.
Getting Started with Android Volley
To integrate Volley in your Android Studio Project, you need to add the following dependency in your build.gradle
file:
compile 'com.android.volley:volley:1.0.0'
Unlike Retrofit, which requires a bunch of libraries like OkHttp, Picasso for complete functionalities, Volley Library is much smaller in size.
Note: With the introduction of Android Studio 3.0 IDE the keyword compile
is replaced by implementation
in the dependencies. Both can be used interchangeably.
The Fundamental classes of Volley are listed below.
RequestQueue
Requests
Response
Android Volley RequestQueue
This is the basic building block of Volley. The name speaks for itself. A RequestQueue is used to queue all the requests and handle the responses. Also, it is responsible for managing the worker threads for parallel requests besides reading and writing to and from the cache. A RequestQueue works in a FIFO manner(First In First Out). At a time it can handle four requests simultaneously. A RequestQueue is instantiated in the following way in a class.
RequestQueue requestQueue = Volley.newRequestQueue(this);
this
refers to the context. It can be activity or application’s context. The above code initializes the RequestQueue with a default set of parameters. We can initialize the RequestQueue with our own set of parameters as shown below.
RequestQueue mRequestQueue;
// Instantiate the cache
Cache cache = new DiskBasedCache(getCacheDir(), 1024 * 1024); // 1MB cap
// Set up the network to use HttpURLConnection as the HTTP client.
Network network = new BasicNetwork(new HurlStack());
// Instantiate the RequestQueue with the cache and network.
mRequestQueue = new RequestQueue(cache, network);
// Start the queue
mRequestQueue.start();
We’ve initialized our own instances of Cache and Network with custom parameters.
Call the method mRequestQueue.stop()
to stop the queue that’ll handle the requests.
Android Volley Request
Request instances are built to define a network request. These are used to support GET and POST requests.
Request class acts as the base class which can be extended to define a custom request.
Volley provides the following types of Requests built-in:
- StringRequest
- JsonObjectRequest
- JsonArrayRequest
- ImageRequest
Android Volley StringRequest
StringRequest is used when you want the response returned in the form of a String. You can then parse the response using Gson or JSON as per your requirement.
StringRequest has two forms of constructors as shown in the image below.
We’ll discuss the first constructor here.
StringRequest(int method, String url, Response.Listener<String> listener, Response.ErrorListener errorListener)
- method : Expects an argument among GET, POST, PUT, DELETE.
- url : URl to fetch the response at.
- listener : Listens for a success response. We need to implement and override the following method in here.
@Override public void onResponse(String response){ //response parameter is of the same type as defined in the constructor. }
- errorListener: Listens for an error response. We need to implement the following method in here.
@Override public void onErrorResponse(VolleyError error){ //handle your error here. We'll look at }
A sample code snippet of a string request is given below.
RequestQueue queue = Volley.newRequestQueue(this);
StringRequest stringRequest = new StringRequest(Request.Method.GET, url, new Response.Listener<String>() {
@Override
public void onResponse(String response) {
VolleyLog.wtf(response, "utf-8");
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
VolleyLog.wtf(error.getMessage(), "utf-8");
}
});
queue.add(stringRequest);
The response or error will be delivered to the onResponse/onErrorResponse classes that we defined in our request above.
queue.add(stringRequest);
is used to add the Request to the RequestQueue.
The code snippet written above can be simplified to improve the readability.
Response.ErrorListener errorListener = new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
VolleyLog.wtf(error.getMessage(), "utf-8");
}
};
Response.Listener<String> responseListener = new Response.Listener<String>() {
@Override
public void onResponse(String response) {
VolleyLog.wtf(response);
}
};
StringRequest stringRequest = new StringRequest(Request.Method.GET, url, responseListener, errorListener);
The constructor looks much better now.
To send parameters in request body we need to override either the getParams()
or getBody()
method of the request class.
Additionally, we can override the getHeaders()
and the getBodyContentType()
methods too to specify the headers and content type formats respectively.
getHeaders()
are useful for authentication.
To override the Request Method type from the first parameter, getMethod()
is implemented.
The constructor with skeleton code of the above mentioned methods is given below.
StringRequest stringRequest = new StringRequest(Request.Method.GET, url, responseListener, errorListener){
@Override
public Map getParams() {
Map params = new HashMap();
params.put("key", "value");
return params;
}
@Override
public byte[] getBody() throws AuthFailureError {
String requestBody = ""; //The request body goes in here.
try {
return requestBody.getBytes("utf-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
VolleyLog.wtf("Unsupported Encoding while trying to get the bytes of %s using %s", requestBody, "utf-8");
return null;
}
}
@Override
public Map getHeaders() throws AuthFailureError {
HashMap headers = new HashMap();
headers.put("Content-Type", "application/x-www-form-urlencoded; charset=utf-8");
String credentials = "sample:[email protected]";
String auth = "Basic "
+ Base64.encodeToString(credentials.getBytes(), Base64.NO_WRAP);
headers.put("Authorization",auth); //authentication
return headers;
}
@Override
public String getBodyContentType() {
return "application/x-www-form-urlencoded; charset=utf-8";
}
@Override
public int getMethod() {
return Method.POST;
}
};
Parameters and headers are specified as a key-value pair in the getParams()
and getHeaders()
methods respectively.
Note: getParams()
method is ignored when the request type is a GET. So to pass parameters we’ll need to concatenate them in the URL string as shown below.
String BASE_URL = "https://reqres.in";
String url = String.format(BASE_URL + "/api/users?page=%1$s", "2");
%1$s
is used for the first param. %2$s
, %3$s
and so on for the second, third parameters.
Android Volley JsonObjectRequest
JsonObjectRequest
is used to send and receive JSONObject from the server. It extends the class JsonRequest.
The two forms of constructors are given below.
We’ll look at the second constructor here.
JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(url, null, new Response.Listener<JSONObject>() {
@Override
public void onResponse(JSONObject response) {
VolleyLog.wtf(response.toString());
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
VolleyLog.wtf(error.getMessage(), "utf-8");
}
});
queue.add(jsonObjectRequest)
An optional JSONObject can be passed as the request body in the second parameter of the constructor.
We’ve set it to null for now.
JsonObjectRequest
can override the same set of methods (getHeaders()
, getBodyContentType()
, getBody()
, getMethod()
) like the StringRequest except getParams()
.
JsonObjectRequest is meant for passing a JSONObject with the request. Hence getParams()
gets ignored.
Overriding getBody()
isn’t neccessary in a JsonObjectRequest since passing a JSONObject as the second parameter does the same implicitly.
If you eventually override getBody()
, the second parameter passed in the constructor would be ignored.
Since the Request type isn’t defined in the above constructor, the request would automatically identify the method(GET,POST, PUT or DELETE) from the url. To explicitily specify the method type we can override the getMethod()
JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(url, null, responseListener, errorListener){
@Override
public int getMethod() {
return Method.POST; //This specifies the Method type to POST explicitly.
}
@Override
public byte[] getBody() {
//Building the response body here would ignore the JSONObject passed in the second parameter.
}
};
Android Volley JsonArrayRequest
JsonArrayRequest is used to send and retrieve JSONArray to and from the server. It extends the JsonRequest class and functions the same way as JsonObjectRequest class.
A JSONArrayRequest can’t handle a request that returns a JSONObject.
Following are the two forms of constructors.
Let’s look at the second constructor.
JsonArrayRequest jsonArrayRequest = new JsonArrayRequest(Request.Method.POST, url, null, new Response.Listener<JSONArray>() {
@Override
public void onResponse(JSONArray response) {
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
VolleyLog.wtf(error.getMessage(), "utf-8");
}
});
Android Volley ImageRequest
Using ImageRequest is the classical and standard way to fetch images from a URL. An ImageRequest returns a Bitmap Object that we can eventually display in our ImageView.
Following are the two constructors present in the class.
Let’s use the second constructor since the first one now stands deprecated.
Besides the url, response and error listeners, the constructor expects the width, height, scaleType and Config values for the bitmap.
RequestQueue queue = Volley.newRequestQueue(this);
ImageRequest imageRequest = new ImageRequest(IMAGE_URL, new Response.Listener<Bitmap>() {
@Override
public void onResponse(Bitmap response) {
if (response != null) {
imageView.setImageBitmap(response);
}
}
}, 200, 200, ImageView.ScaleType.CENTER_CROP, Bitmap.Config.ARGB_8888, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
VolleyLog.wtf(error.getMessage(), "utf-8");
}
});
queue.add(imageRequest);
The RequestQueue has a RequestFinishedListener. The listener gets triggered everytime a request in the queue is finished.
queue.addRequestFinishedListener(new RequestQueue.RequestFinishedListener<Object>() {
@Override
public void onRequestFinished(Request<Object> request) {
try {
if (request.getCacheEntry() != null) {
String cacheValue = new String(request.getCacheEntry().data, "UTF-8");
Log.d(TAG, request.getCacheKey() + " " + cacheValue);
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
});
Android Volley Error Handling
onErrorResponse()
passes an instance of VolleyError as we have seen above.
Following are the major types of errors that can be returned in the instance.
- AuthFailureError: When the authentication fails.
- NetworkError: Server Error, DNS issues etc.
- NoConnectionError: When there is no internet connection.
- ParseError: Generally returned in a JsonObjectRequest or JsonArrayRequest when the JSON response is malformed.
- ServerError: The server responded with an error status code (401, 500 etc)
- TimeoutError: When a request timeout occurs. Volley has a default timeout of 2.5 seconds.
Android Volley Cancelling Requests
Volley has a powerful API for canceling requests. Request cancellation is important in situations where the Activity needs to be destroyed. Cancellation of requests is possible using tags.
JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(url, null, responseListener, errorListener);
jsonObjectRequest.setTag("Sample");
JsonObjectRequest jsonObjectRequest2 = new JsonObjectRequest(url_2, null, responseListener, errorListener);
jsonObjectRequest2.setTag("No sample");
queue.add(jsonObjectRequest);
queue.add(jsonObjectRequest2);
queue.cancelAll("Sample"); //Cancels all requests with tag "Sample" present in the RequestQueue.
//Alternative way....
queue.cancelAll(new RequestQueue.RequestFilter() {
@Override
public boolean apply(Request<?> request) {
return request.getTag().equals("Sample"); //requests with the condition true are cancelled.
}
});
Android Volley Prioritizing Requests
A priority can be set on each request present in the RequestQueue. Doing so the RequestQueue would run the requests based on the priorities instead of FIFO. A priority can be assigned by overriding the method getPriority()
in the constructor. Following are the possible states:
- Priority.LOW: Used to load images.
- Priority.NORMAL: Default value.
- Priority.HIGH: Used to load texts.
- Priority.IMMEDIATE: Used in places like login/logout.
Following code snippet assigns priority to requests.
RequestQueue queue = Volley.newRequestQueue(this);
Response.Listener<JSONObject> jsonResponseListener = new Response.Listener<JSONObject>() {
@Override
public void onResponse(JSONObject response) {
}
};
Response.Listener<String> stringResponseListener = new Response.Listener<String>() {
@Override
public void onResponse(String response) {
}
};
Response.Listener imageResponseListener = new Response.Listener() {
@Override
public void onResponse(Bitmap response) {
}
};
Response.ErrorListener errorListener = new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
VolleyLog.wtf(error.getMessage(), "utf-8");
}
};
StringRequest stringRequest = new StringRequest(Request.Method.GET, url, stringResponseListener, errorListener){
@Override
public Priority getPriority() {
return Priority.IMMEDIATE;
}};
JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(url, null, jsonResponseListener, errorListener){
@Override
public Priority getPriority() {
return Priority.HIGH;
}};;
ImageRequest imageRequest = new ImageRequest(IMAGE_URL, imageResponseListener, 200, 200, ImageView.ScaleType.CENTER_CROP, Bitmap.Config.ARGB_8888, errorListener){
@Override
public Priority getPriority() {
return Priority.LOW;
}};;
queue.add(imageRequest);
queue.add(jsonObjectRequest);
queue.add(stringRequest);
//Order of requests run : stringRequest > jsonRequest > imageRequest
Android Volley Caching
Caches are enabled by default for the above mentioned Request types. The response data is typically stored in the form of byte array
Disabling Cache
stringRequest.setShouldCache(false)
Accessing the Cache
String cacheValue = new String(stringRequest.getCacheEntry().data, "UTF-8");
//Accessing from the RequestQueue
String cacheValue = new String(queue.getCache().get(url).data, "UTF-8");
//The Request URL is stored in the cache key ...
String url = request.getCacheKey();
//Check other details in the cache.
stringRequest.getCacheEntry().isExpired() //checks if the cache is expired or not.
stringRequest.getCacheEntry().lastModified
Clearing cache
queue.getCache().remove(url); //removes cache for the specific url.
//clear all cache....
queue.getCache().clear();
Invalidate cache: Invalidating a cache would show the cached data until the new response is returned. That response would override the current cache.
queue.getCache().invalidate(url, true);
Android Volley ImageLoader and NetworkImageView
We’ve used an ImageRequest to retrieve an image bitmap from the response before.
Volley provides us with NetworkImageView widget that directly loads the image from the URL. Besides, it lets us set a default placeholder as well as an error drawable.
ImageLoader is responsible for loading the images using a BitmapLruCache()
.
<com.android.volley.toolbox.NetworkImageView
android:id="@+id/networkImageView"
android:layout_width="100dp"
android:layout_height="100dp"/>
setImageFromUrl()
method directly sets the image returned from the url onto the NetworkImageView instance. We need to pass the URL and ImageLoader instance in this method to fetch the image.
networkImageView.setDefaultImageResId(R.mipmap.ic_launcher);
networkImageView.setErrorImageResId(R.drawable.ic_error);
ImageLoader imageLoader = new ImageLoader(mRequestQueue, new BitmapLruCache(64000)); //size is in Kb.
networkImageView.setImageUrl(IMAGE_URL, imageLoader);
// Loads the image with placeholder and error image
imageLoader.get(IMAGE_URL, ImageLoader.getImageListener(networkImageView, R.mipmap.ic_launcher, R.drawable.ic_error));
Note: To troubleshoot and log debug traces set the following line in your code.
VolleyLog.DEBUG = true;
We can create our own Custom Requests too by extending Request<T>. The two abstract methods parseNetworkResponse()
and deliverResponse()
need to be overriden.
This brings an end to this comprehensive tutorial on Volley. We’ll implement Volley in an application using the above-learned concepts in our next tutorial.
Reference: GitHub Page, API Doc