Transmitting Network Data Using Volley
DEPENDENCIES AND PREREQUISITES
- Android 1.6 (API Level 4) or higher
VIDEO
Volley: Easy, Fast Networking for Android
Volley is an HTTP library that makes networking for Android apps easier and most importantly, faster. Volley is available through the open AOSP repository.
Volley offers the following benefits:
- Automatic scheduling of network requests.
- Multiple concurrent network connections.
- Transparent disk and memory response caching with standard HTTP cache coherence.
- Support for request prioritization.
- Cancellation request API. You can cancel a single request, or you can set blocks or scopes of requests to cancel.
- Ease of customization, for example, for retry and backoff.
- Strong ordering that makes it easy to correctly populate your UI with data fetched asynchronously from the network.
- Debugging and tracing tools.
Volley excels at RPC-type operations used to populate a UI, such as fetching a page of search results as structured data. It integrates easily with any protocol and comes out of the box with support for raw strings, images, and JSON. By providing built-in support for the features you need, Volley frees you from writing boilerplate code and allows you to concentrate on the logic that is specific to your app.
Volley is not suitable for large download or streaming operations, since Volley holds all responses in memory during parsing. For large download operations, consider using an alternative like DownloadManager
.
The core Volley library is developed in the open AOSP repository at frameworks/volley
and contains the main request dispatch pipeline as well as a set of commonly applicable utilities, available in the Volley "toolbox." The easiest way to add Volley to your project is to clone the Volley repository and set it as a library project:
- Git clone the repository by typing the following at the command line:
git clone https://android.googlesource.com/platform/frameworks/volley
- Import the downloaded source into your app project as an Android library project (as described in Managing Projects from Eclipse with ADT, if you're using Eclipse) or make a
.jar
file.
Lessons
- Sending a Simple Request
- Learn how to send a simple request using the default behaviors of Volley, and how to cancel a request.
- Setting Up a RequestQueue
- Learn how to set up a
RequestQueue
, and how to implement a singleton pattern to create aRequestQueue
that lasts the lifetime of your app. - Making a Standard Request
- Learn how to send a request using one of Volley's out-of-the-box request types (raw strings, images, and JSON).
- Implementing a Custom Request
- Learn how to implement a custom request.
Sending a Simple Request
THIS LESSON TEACHES YOU TO
VIDEO
Volley: Easy, Fast Networking for Android
At a high level, you use Volley by creating a RequestQueue
and passing it Request
objects. The RequestQueue
manages worker threads for running the network operations, reading from and writing to the cache, and parsing responses. Requests do the parsing of raw responses and Volley takes care of dispatching the parsed response back to the main thread for delivery.
This lesson describes how to send a request using theVolley.newRequestQueue
convenience method, which sets up aRequestQueue
for you. See the next lesson, Setting Up a RequestQueue, for information on how to set up a RequestQueue
yourself.
This lesson also describes how to add a request to aRequestQueue
and cancel a request.
Add the INTERNET Permission
To use Volley, you must add the android.permission.INTERNET
permission to your app's manifest. Without this, your app won't be able to connect to the network.
Use newRequestQueue
Volley provides a convenience method Volley.newRequestQueue
that sets up a RequestQueue
for you, using default values, and starts the queue. For example:
final TextView mTextView = (TextView) findViewById(R.id.text);
... // Instantiate the RequestQueue.
RequestQueue queue = Volley.newRequestQueue(this);
String url ="http://www.google.com"; // Request a string response from the provided URL.
StringRequest stringRequest = new StringRequest(Request.Method.GET, url,
new Response.Listener<String>() {
@Override
public void onResponse(String response) {
// Display the first 500 characters of the response string.
mTextView.setText("Response is: "+ response.substring(0,500));
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
mTextView.setText("That didn't work!");
}
});
// Add the request to the RequestQueue.
queue.add(stringRequest);
Volley always delivers parsed responses on the main thread. Running on the main thread is convenient for populating UI controls with received data, as you can freely modify UI controls directly from your response handler, but it's especially critical to many of the important semantics provided by the library, particularly related to canceling requests.
See Setting Up a RequestQueue for a description of how to set up a RequestQueue
yourself, instead of using theVolley.newRequestQueue
convenience method.
Send a Request
To send a request, you simply construct one and add it to the RequestQueue
with add()
, as shown above. Once you add the request it moves through the pipeline, gets serviced, and has its raw response parsed and delivered.
When you call add()
, Volley runs one cache processing thread and a pool of network dispatch threads. When you add a request to the queue, it is picked up by the cache thread and triaged: if the request can be serviced from cache, the cached response is parsed on the cache thread and the parsed response is delivered on the main thread. If the request cannot be serviced from cache, it is placed on the network queue. The first available network thread takes the request from the queue, performs the HTTP transaction, parsse the response on the worker thread, writes the response to cache, and posts the parsed response back to the main thread for delivery.
Note that expensive operations like blocking I/O and parsing/decoding are done on worker threads. You can add a request from any thread, but responses are always delivered on the main thread.
Figure 1 illustrates the life of a request:
Figure 1. Life of a request.
Cancel a Request
To cancel a request, call cancel()
on your Request
object. Once cancelled, Volley guarantees that your response handler will never be called. What this means in practice is that you can cancel all of your pending requests in your activity's onStop()
method and you don't have to litter your response handlers with checks for getActivity() == null
, whether onSaveInstanceState()
has been called already, or other defensive boilerplate.
To take advantage of this behavior, you would typically have to track all in-flight requests in order to be able to cancel them at the appropriate time. There is an easier way: you can associate a tag object with each request. You can then use this tag to provide a scope of requests to cancel. For example, you can tag all of your requests with the Activity
they are being made on behalf of, and call requestQueue.cancelAll(this)
from onStop()
. Similarly, you could tag all thumbnail image requests in a ViewPager
tab with their respective tabs and cancel on swipe to make sure that the new tab isn't being held up by requests from another one.
Here is an example that uses a string value for the tag:
- Define your tag and add it to your requests.
public static final String TAG = "MyTag";
StringRequest stringRequest; // Assume this exists.
RequestQueue mRequestQueue; // Assume this exists. // Set the tag on the request.
stringRequest.setTag(TAG); // Add the request to the RequestQueue.
mRequestQueue.add(stringRequest); - In your activity's
onStop()
method, cancel all requests that have this tag.@Override
protected void onStop () {
super.onStop();
if (mRequestQueue != null) {
mRequestQueue.cancelAll(TAG);
}
}
Take care when canceling requests. If you are depending on your response handler to advance a state or kick off another process, you need to account for this. Again, the response handler will not be called.
Setting Up a RequestQueue
THIS LESSON TEACHES YOU TO
VIDEO
Volley: Easy, Fast Networking for Android
The previous lesson showed you how to use the convenience method Volley.newRequestQueue
to set up a RequestQueue
, taking advantage of Volley's default behaviors. This lesson walks you through the explicit steps of creating a RequestQueue
, to allow you to supply your own custom behavior.
This lesson also describes the recommended practice of creating a RequestQueue
as a singleton, which makes the RequestQueue
last the lifetime of your app.
Set Up a Network and Cache
A RequestQueue
needs two things to do its job: a network to perform transport of the requests, and a cache to handle caching. There are standard implementations of these available in the Volley toolbox: DiskBasedCache
provides a one-file-per-response cache with an in-memory index, and BasicNetwork
provides a network transport based on your choice of AndroidHttpClient
or HttpURLConnection
.
BasicNetwork
is Volley's default network implementation. A BasicNetwork
must be initialized with the HTTP client your app is using to connect to the network. Typically this is AndroidHttpClient
or HttpURLConnection
:
- Use
AndroidHttpClient
for apps targeting Android API levels lower than API Level 9 (Gingerbread). Prior to Gingerbread,HttpURLConnection
was unreliable. For more discussion of this topic, see Android's HTTP Clients. - Use
HttpURLConnection
for apps targeting Android API Level 9 (Gingerbread) and higher.
To create an app that runs on all versions of Android, you can check the version of Android the device is running and choose the appropriate HTTP client, for example:
HttpStack stack;
...
// If the device is running a version >= Gingerbread...
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
// ...use HttpURLConnection for stack.
} else {
// ...use AndroidHttpClient for stack.
}
Network network = new BasicNetwork(stack);
This snippet shows you the steps involved in setting up a RequestQueue
:
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(); String url ="http://www.myurl.com"; // Formulate the request and handle the response.
StringRequest stringRequest = new StringRequest(Request.Method.GET, url,
new Response.Listener<String>() {
@Override
public void onResponse(String response) {
// Do something with the response
}
},
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
// Handle error
}
}); // Add the request to the RequestQueue.
mRequestQueue.add(stringRequest);
...
If you just need to make a one-time request and don't want to leave the thread pool around, you can create theRequestQueue
wherever you need it and call stop()
on the RequestQueue
once your response or error has come back, using the Volley.newRequestQueue()
method described in Sending a Simple Request. But the more common use case is to create the RequestQueue
as a singleton to keep it running for the lifetime of your app, as described in the next section.
Use a Singleton Pattern
If your application makes constant use of the network, it's probably most efficient to set up a single instance ofRequestQueue
that will last the lifetime of your app. You can achieve this in various ways. The recommended approach is to implement a singleton class that encapsulates RequestQueue
and other Volley functionality. Another approach is to subclass Application
and set up the RequestQueue
in Application.onCreate()
. But this approach is discouraged; a static singleton can provide the same functionality in a more modular way.
A key concept is that the RequestQueue
must be instantiated with the Application
context, not an Activity
context. This ensures that the RequestQueue
will last for the lifetime of your app, instead of being recreated every time the activity is recreated (for example, when the user rotates the device).
Here is an example of a singleton class that provides RequestQueue
and ImageLoader
functionality:
private static MySingleton mInstance;
private RequestQueue mRequestQueue;
private ImageLoader mImageLoader;
private static Context mCtx; private MySingleton(Context context) {
mCtx = context;
mRequestQueue = getRequestQueue(); mImageLoader = new ImageLoader(mRequestQueue,
new ImageLoader.ImageCache() {
private final LruCache<String, Bitmap>
cache = new LruCache<String, Bitmap>(20); @Override
public Bitmap getBitmap(String url) {
return cache.get(url);
} @Override
public void putBitmap(String url, Bitmap bitmap) {
cache.put(url, bitmap);
}
});
} public static synchronized MySingleton getInstance(Context context) {
if (mInstance == null) {
mInstance = new MySingleton(context);
}
return mInstance;
} public RequestQueue getRequestQueue() {
if (mRequestQueue == null) {
// getApplicationContext() is key, it keeps you from leaking the
// Activity or BroadcastReceiver if someone passes one in.
mRequestQueue = Volley.newRequestQueue(mCtx.getApplicationContext());
}
return mRequestQueue;
} public <T> void addToRequestQueue(Request<T> req) {
getRequestQueue().add(req);
} public ImageLoader getImageLoader() {
return mImageLoader;
}
}
Here are some examples of performing RequestQueue
operations using the singleton class:
// Get a RequestQueue
RequestQueue queue = MySingleton.getInstance(this.getApplicationContext()).
getRequestQueue();
... // Add a request (in this example, called stringRequest) to your RequestQueue.
MySingleton.getInstance(this).addToRequestQueue(stringRequest);
Making a Standard Request
THIS LESSON TEACHES YOU TO
VIDEO
Volley: Easy, Fast Networking for Android
This lesson describes how to use the common request types that Volley supports:
-
StringRequest
. Specify a URL and receive a raw string in response. See Setting Up a Request Queue for an example. -
ImageRequest
. Specify a URL and receive an image in response. -
JsonObjectRequest
andJsonArrayRequest
(both subclasses ofJsonRequest
). Specify a URL and get a JSON object or array (respectively) in response.
If your expected response is one of these types, you probably won't have to implement a custom request. This lesson describes how to use these standard request types. For information on how to implement your own custom request, see Implementing a Custom Request.
Request an Image
Volley offers the following classes for requesting images. These classes layer on top of each other to offer different levels of support for processing images:
-
ImageRequest
—a canned request for getting an image at a given URL and calling back with a decoded bitmap. It also provides convenience features like specifying a size to resize to. Its main benefit is that Volley's thread scheduling ensures that expensive image operations (decoding, resizing) automatically happen on a worker thread. -
ImageLoader
—a helper class that handles loading and caching images from remote URLs.ImageLoader
is a an orchestrator for large numbers ofImageRequest
s, for example when putting multiple thumbnails in aListView
.ImageLoader
provides an in-memory cache to sit in front of the normal Volley cache, which is important to prevent flickering. This makes it possible to achieve a cache hit without blocking or deferring off the main thread, which is impossible when using disk I/O.ImageLoader
also does response coalescing, without which almost every response handler would set a bitmap on a view and cause a layout pass per image. Coalescing makes it possible to deliver multiple responses simultaneously, which improves performance. -
NetworkImageView
—builds onImageLoader
and effectively replacesImageView
for situations where your image is being fetched over the network via URL.NetworkImageView
also manages canceling pending requests if the view is detached from the hierarchy.
Use ImageRequest
Here is an example of using ImageRequest
. It retrieves the image specified by the URL and displays it in the app. Note that this snippet interacts with the RequestQueue
through a singleton class (see Setting Up a RequestQueuefor more discussion of this topic):
ImageView mImageView;
String url = "http://i.imgur.com/7spzG.png";
mImageView = (ImageView) findViewById(R.id.myImage);
... // Retrieves an image specified by the URL, displays it in the UI.
ImageRequest request = new ImageRequest(url,
new Response.Listener<Bitmap>() {
@Override
public void onResponse(Bitmap bitmap) {
mImageView.setImageBitmap(bitmap);
}
}, 0, 0, null,
new Response.ErrorListener() {
public void onErrorResponse(VolleyError error) {
mImageView.setImageResource(R.drawable.image_load_error);
}
});
// Access the RequestQueue through your singleton class.
MySingleton.getInstance(this).addToRequestQueue(request);
Use ImageLoader and NetworkImageView
You can use ImageLoader
and NetworkImageView
in concert to efficiently manage the display of multiple images, such as in a ListView
. In your layout XML file, you use NetworkImageView
in much the same way you would useImageView
, for example:
<com.android.volley.toolbox.NetworkImageView
android:id="@+id/networkImageView"
android:layout_width="150dp"
android:layout_height="170dp"
android:layout_centerHorizontal="true" />
You can use ImageLoader
by itself to display an image, for example:
ImageLoader mImageLoader;
ImageView mImageView;
// The URL for the image that is being loaded.
private static final String IMAGE_URL =
"http://developer.android.com/images/training/system-ui.png";
...
mImageView = (ImageView) findViewById(R.id.regularImageView); // Get the ImageLoader through your singleton class.
mImageLoader = MySingleton.getInstance(this).getImageLoader();
mImageLoader.get(IMAGE_URL, ImageLoader.getImageListener(mImageView,
R.drawable.def_image, R.drawable.err_image));
However, NetworkImageView
can do this for you if all you're doing is populating an ImageView
. For example:
ImageLoader mImageLoader;
NetworkImageView mNetworkImageView;
private static final String IMAGE_URL =
"http://developer.android.com/images/training/system-ui.png";
... // Get the NetworkImageView that will display the image.
mNetworkImageView = (NetworkImageView) findViewById(R.id.networkImageView); // Get the ImageLoader through your singleton class.
mImageLoader = MySingleton.getInstance(this).getImageLoader(); // Set the URL of the image that should be loaded into this view, and
// specify the ImageLoader that will be used to make the request.
mNetworkImageView.setImageUrl(IMAGE_URL, mImageLoader);
The above snippets access the RequestQueue
and the ImageLoader
through a singleton class, as described inSetting Up a RequestQueue. This approach ensures that your app creates single instances of these classes that last the lifetime of your app. The reason that this is important for ImageLoader
(the helper class that handles loading and caching images) is that the main function of the in-memory cache is to allow for flickerless rotation. Using a singleton pattern allows the bitmap cache to outlive the activity. If instead you create the ImageLoader
in an activity, the ImageLoader
would be recreated along with the activity every time the user rotates the device. This would cause flickering.
Example LRU cache
The Volley toolbox provides a standard cache implementation via the DiskBasedCache
class. This class caches files directly onto the hard disk in the specified directory. But to use ImageLoader
, you should provide a custom in-memory LRU bitmap cache that implements the ImageLoader.ImageCache
interface. You may want to set up your cache as a singleton; for more discussion of this topic, see Setting Up a RequestQueue.
Here is a sample implementation for an in-memory LruBitmapCache
class. It extends the LruCache
class and implements the ImageLoader.ImageCache
interface:
import android.graphics.Bitmap;
import android.support.v4.util.LruCache;
import android.util.DisplayMetrics;
import com.android.volley.toolbox.ImageLoader.ImageCache; public class LruBitmapCache extends LruCache<String, Bitmap>
implements ImageCache { public LruBitmapCache(int maxSize) {
super(maxSize);
} public LruBitmapCache(Context ctx) {
this(getCacheSize(ctx));
} @Override
protected int sizeOf(String key, Bitmap value) {
return value.getRowBytes() * value.getHeight();
} @Override
public Bitmap getBitmap(String url) {
return get(url);
} @Override
public void putBitmap(String url, Bitmap bitmap) {
put(url, bitmap);
} // Returns a cache size equal to approximately three screens worth of images.
public static int getCacheSize(Context ctx) {
final DisplayMetrics displayMetrics = ctx.getResources().
getDisplayMetrics();
final int screenWidth = displayMetrics.widthPixels;
final int screenHeight = displayMetrics.heightPixels;
// 4 bytes per pixel
final int screenBytes = screenWidth * screenHeight * 4; return screenBytes * 3;
}
}
Here is an example of how to instantiate an ImageLoader
to use this cache:
RequestQueue mRequestQueue; // assume this exists.
ImageLoader mImageLoader = new ImageLoader(mRequestQueue, new LruBitmapCache(
LruBitmapCache.getCacheSize()));
Request JSON
Volley provides the following classes for JSON requests:
-
JsonArrayRequest
—A request for retrieving aJSONArray
response body at a given URL. -
JsonObjectRequest
—A request for retrieving aJSONObject
response body at a given URL, allowing for an optionalJSONObject
to be passed in as part of the request body.
Both classes are based on the common base class JsonRequest
. You use them following the same basic pattern you use for other types of requests. For example, this snippet fetches a JSON feed and displays it as text in the UI:
TextView mTxtDisplay;
ImageView mImageView;
mTxtDisplay = (TextView) findViewById(R.id.txtDisplay);
String url = "http://my-json-feed"; JsonObjectRequest jsObjRequest = new JsonObjectRequest
(Request.Method.GET, url, null, new Response.Listener<JSONObject>() { @Override
public void onResponse(JSONObject response) {
mTxtDisplay.setText("Response: " + response.toString());
}
}, new Response.ErrorListener() { @Override
public void onErrorResponse(VolleyError error) {
// TODO Auto-generated method stub }
}); // Access the RequestQueue through your singleton class.
MySingleton.getInstance(this).addToRequestQueue(jsObjRequest);
For an example of implementing a custom JSON request based on Gson, see the next lesson, Implementing a Custom Request.
Implementing a Custom Request
THIS LESSON TEACHES YOU TO
VIDEO
Volley: Easy, Fast Networking for Android
This lesson describes how to implement your own custom request types, for types that don't have out-of-the-box Volley support.
Write a Custom Request
Most requests have ready-to-use implementations in the toolbox; if your response is a string, image, or JSON, you probably won't need to implement a custom
Request
.
For cases where you do need to implement a custom request, this is all you need to do:
- Extend the
Request<T>
class, where<T>
represents the type of parsed response the request expects. So if your parsed response is a string, for example, create your custom request by extendingRequest<String>
. See the Volley toolbox classesStringRequest
andImageRequest
for examples of extendingRequest<T>
. - Implement the abstract methods
parseNetworkResponse()
anddeliverResponse()
, described in more detail below.
parseNetworkResponse
A Response
encapsulates a parsed response for delivery, for a given type (such as string, image, or JSON). Here is a sample implementation of parseNetworkResponse()
:
@Override
protected Response<T> parseNetworkResponse(
NetworkResponse response) {
try {
String json = new String(response.data,
HttpHeaderParser.parseCharset(response.headers));
return Response.success(gson.fromJson(json, clazz),
HttpHeaderParser.parseCacheHeaders(response));
}
// handle errors
...
}
Note the following:
-
parseNetworkResponse()
takes as its parameter aNetworkResponse
, which contains the response payload as a byte[], HTTP status code, and response headers. - Your implementation must return a
Response<T>
, which contains your typed response object and cache metadata or an error, such as in the case of a parse failure.
If your protocol has non-standard cache semantics, you can build a Cache.Entry
yourself, but most requests are fine with something like this:
return Response.success(myDecodedObject,
HttpHeaderParser.parseCacheHeaders(response));
Volley calls parseNetworkResponse()
from a worker thread. This ensures that expensive parsing operations, such as decoding a JPEG into a Bitmap, don't block the UI thread.
deliverResponse
Volley calls you back on the main thread with the object you returned in parseNetworkResponse()
. Most requests invoke a callback interface here, for example:
protected void deliverResponse(T response) {
listener.onResponse(response);
Example: GsonRequest
Gson is a library for converting Java objects to and from JSON using reflection. You can define Java objects that have the same names as their corresponding JSON keys, pass Gson the class object, and Gson will fill in the fields for you. Here's a complete implementation of a Volley request that uses Gson for parsing:
public class GsonRequest<T> extends Request<T> {
private final Gson gson = new Gson();
private final Class<T> clazz;
private final Map<String, String> headers;
private final Listener<T> listener; /**
* Make a GET request and return a parsed object from JSON.
*
* @param url URL of the request to make
* @param clazz Relevant class object, for Gson's reflection
* @param headers Map of request headers
*/
public GsonRequest(String url, Class<T> clazz, Map<String, String> headers,
Listener<T> listener, ErrorListener errorListener) {
super(Method.GET, url, errorListener);
this.clazz = clazz;
this.headers = headers;
this.listener = listener;
} @Override
public Map<String, String> getHeaders() throws AuthFailureError {
return headers != null ? headers : super.getHeaders();
} @Override
protected void deliverResponse(T response) {
listener.onResponse(response);
} @Override
protected Response<T> parseNetworkResponse(NetworkResponse response) {
try {
String json = new String(
response.data,
HttpHeaderParser.parseCharset(response.headers));
return Response.success(
gson.fromJson(json, clazz),
HttpHeaderParser.parseCacheHeaders(response));
} catch (UnsupportedEncodingException e) {
return Response.error(new ParseError(e));
} catch (JsonSyntaxException e) {
return Response.error(new ParseError(e));
}
}
}
Volley provides ready-to-use JsonArrayRequest
and JsonArrayObject
classes if you prefer to take that approach. See Using Standard Request Types for more information.