Volley is a new Android networking library from Google (well, by ‘new’ I mean from May, at I/O 2013 - so some 7 months ago). It has some cool features - request queueing with priorities, automatic selection of the best HTTP library depending on Android version, and a nifty view for automatically loading images. Unfortunately, even 7 months on, there’s pretty minimal documentation available. However across StackOverflow, a bunch of blogs and the source code, there’s plenty to go on to figure out how to do some basic tasks.
Now, we have volley.jar in the bin directory. Copy this into libs in Eclipse (via drag & drop through the GUI so it sets everything up properly) or set it up in your build.gradle if you’re using Studio. Wonderful.
Some basic setup
Volley works by sending requests to a RequestQueue. To create one of these requests, you override the Request object and implement a few methods:
12345678910111213
queue.add(newRequest<String>(Method.GET,url,errorListener){@OverrideprotectedResponse<String>parseNetworkResponse(NetworkResponseresponse){// TODO Auto-generated method stubreturnnull;}@OverrideprotectedvoiddeliverResponse(Stringresponse){// TODO Auto-generated method stub}});
You can send these with a priority (by overriding getPriority()), so that higher priority requests get sent to the front of the queue, not the back. Useful if, for example, you’re loading some images in the background, but the user clicks on something that needs immediate download.
Before we can use a queue, we have to set one up. This should done as a singleton. Since the easiest way of creating a RequestQueue requires a Context, you can either subclass Application (which the official docs advise against) or do it this way:
There are a few other ways of constructing a queue, allowing you to specify your own HTTP stack, cache, thread pool size, etc.
Now let’s GET some JSON
So now we’ve got our default queue set up, we can send a request. As you saw earlier, a request requires you to implement two methods - parseNetworkResponse and deliverResponse. The first of these methods parses your network response into some object that you’re expecting, from a worker thread. The second delivers that response back to your UI thread, unless parseNetworkResponse returns null.
To fetch some simple JSON back from a given URL, there’s a convenient utility class that comes packaged in com.android.volley.toolbox called JsonRequest.
This class manages parsing any request body string into a byte array (the getBody method returns a byte[]), as well as specifying the content type headers, etc. You still have to implement the parseNetworkResponse abstract method from before, though you now supply a listener for the success case instead of an override. So now our request looks a bit like this (using Gson for parsing the response, because it’s awesome):
publicclassPerson{longid;StringfirstName;StringlastName;Stringaddress;}...Stringurl="http://person.com/person?id=1234";Requestrequest=newJsonRequest<Person>(Method.GET,url,null,newListener<Person>(){@OverridepublicvoidonResponse(Personresponse){// Do something with our person object }},newResponse.ErrorListener(){@OverridepublicvoidonErrorResponse(VolleyErrorerror){// Handle the error // error.networkResponse.statusCode// error.networkResponse.data}}){@OverrideprotectedResponse<Person>parseNetworkResponse(NetworkResponseresponse){StringjsonString=newString(response.data,HttpHeaderParser.parseCharset(response.headers));Personperson=newGsonBuilder().create().fromJson(jsonString,Person.class);Response<Person>result=Response.success(person,HttpHeaderParser.parseCacheHeaders(response));returnresult;}};queue.add(request);
And POST some back to the server
POSTing JSON back is equally as easy! Instead of passing in null for our body, we pass in a JSON String.
1234567891011121314151617181920212223242526
Stringurl="http://person.com/person/update?id=1234";Stringbody=newGsonBuilder().create().toJson(somePerson);Requestrequest=newJsonRequest<Person>(Method.POST,url,body,newListener<Person>(){@OverridepublicvoidonResponse(Personresponse){// Do something with our person object }},newResponse.ErrorListener(){@OverridepublicvoidonErrorResponse(VolleyErrorerror){// Handle the error // error.networkResponse.statusCode// error.networkResponse.data}}){@OverrideprotectedResponse<Person>parseNetworkResponse(NetworkResponseresponse){StringjsonString=newString(response.data,HttpHeaderParser.parseCharset(response.headers));Personperson=newGsonBuilder().create().fromJson(jsonString,Person.class);Response<Person>result=Response.success(person,HttpHeaderParser.parseCacheHeaders(response));returnresult;}};
Is there an easier way?
Sort-of. I’ve abstracted the JSON parsing out so you only have to handle the success and the failure cases. If you’ve got more complex objects that need custom type adapters, you could put the Gson object creation and type adapter registration into another class somewhere and call it from here. Just drop this GsonRequest class in and you can use it by simply passing in the class of object you expect, as follows.
/** * Copyright 2013 Adam Speakman * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */packageyour.package.here;importjava.io.UnsupportedEncodingException;importcom.android.volley.NetworkResponse;importcom.android.volley.ParseError;importcom.android.volley.Response;importcom.android.volley.Response.ErrorListener;importcom.android.volley.Response.Listener;importcom.android.volley.toolbox.HttpHeaderParser;importcom.android.volley.toolbox.JsonRequest;importcom.google.gson.GsonBuilder;importcom.google.gson.JsonSyntaxException;publicclassGsonRequest<T>extendsJsonRequest<T>{Class<T>mResponseClass;publicGsonRequest(intmethod,Stringurl,StringrequestBody,Class<T>responseClass,Listener<T>listener,ErrorListenererrorListener){super(method,url,requestBody,listener,errorListener);mResponseClass=responseClass;// Do some generic stuff in here - for example, set your retry policy to// longer if you know all your requests are going to take > 2.5 seconds// etc etc...}@OverrideprotectedResponse<T>parseNetworkResponse(NetworkResponsenetworkResponse){try{StringjsonString=newString(networkResponse.data,HttpHeaderParser.parseCharset(networkResponse.headers));Tresponse=newGsonBuilder().create().fromJson(jsonString,mResponseClass);com.android.volley.Response<T>result=com.android.volley.Response.success(response,HttpHeaderParser.parseCacheHeaders(networkResponse));returnresult;}catch(UnsupportedEncodingExceptione){returncom.android.volley.Response.error(newParseError(e));}catch(JsonSyntaxExceptione){returncom.android.volley.Response.error(newParseError(e));}}}
And usage:
123456789101112131415
Requestrequest=newGsonRequest<T>(Method.POST,url,body,Person.class,newListener<Person>(){@OverridepublicvoidonResponse(Personresponse){// Do something with our person object }},newResponse.ErrorListener(){@OverridepublicvoidonErrorResponse(VolleyErrorerror){// Handle the error // error.networkResponse.statusCode// error.networkResponse.data}});
What about that easy image loading?
Loading images with Volley is one of my favourite features of the library. Once it’s setup, it’s really easy to use. It handles loading images off the UI thread, can show a default image and an error one, and handles caching all for you. You need to set up an ImageLoader, similar to how you set up the RequestQueue as a singleton:
ImageLoaderProvider.java
123456789101112131415161718192021222324
publicclassImageLoaderProvider{privatestaticImageLoaderimageLoader=null;privateImageLoaderProvider(){}publicstaticsynchronizedImageLoadergetImageLoader(Contextctx,RequestQueuequeue){if(imageLoader==null){imageLoader=newImageLoader(queue,newLruBitmapCache(getCacheSize(ctx)));}returnimageLoader;}/** * Returns a cache size equal to approximately three screens worth of images. */privateintgetCacheSize(Contextctx){finalDisplayMetricsdisplayMetrics=ctx.getResources().getDisplayMetrics();finalintscreenWidth=displayMetrics.widthPixels;finalintscreenHeight=displayMetrics.heightPixels;finalintscreenBytes=screenWidth*screenHeight*4;// 4 bytes per pixelreturnscreenBytes*3;}}
Now, replace your ImageView objects with NetworkImageView ones:
There are also methods available such as setDefaultImageResId and setErrorImageResId, for supplying default and error resources.
Anything else?
Volley has a default TTL on requests of 2.5 seconds - after this, it’ll retry the request. This can result in some unexpected behaviour - for example where your error listener gets called (immediately after the retry), then your success listener gets called a little while later (when the original request returns). You can fix this by specifying a timeout in your request:
Another thing to be aware of is that the image loading will cache images in the full size they come down as. This means if you’re downloading images at full resolution but only displaying them at a much smaller one, you’re going to be caching them at full res. If this is in a list view, you’re going to be pushing stuff out of the cache (and then re-downloading them) a lot more often than desirable.
You can get around this by changing some code in NetworkImageView. The important bit is near the end of the loadImageIfNecessary(final boolean isInLayoutPass) method. The code makes a call to the following method in the ImageLoader class:
Well, if we ‘fix’ the code back in NetworkImageView to pass in the width & height of the view, then the image gets scaled down and cached at the smaller size (this takes is utilised in the doParse method of ImageRequest:
Note that if you pass the image URL in for use in a different place, it’ll use the (scaled down) image from the cache - so if you need the full resolution image, this solution will need some modification.
Wow, Volley can do lots of stuff!
And I certainly haven’t covered all of it here. There’s loads more that it can do. I’d recommend looking through some of the classes in com.android.volley.toolbox to see what else is already written for you, and for some ideas of how to use some of the other cool features it has to offer.