Recently, at a client, I was tasked with evaluating networking frameworks to potentially replace the client application's implemented Spring Framework. The idea was to provide an upgrade that could be realized without too much of a code overhaul while also providing features, such as the ability to make asynchronous calls, to be leveraged in the future. The first framework that instantly came to mind was Volley. I have successfully implemented Volley in other production applications and have posted a blog on the subject. That being said, I was going into my evaluation quite biased. Undoubtedly, a decision that I will likely live with for the next two years (or more) cannot and should not be made off a bias. Then along came Retrofit with all of its fancy documentation and in its shiny Square packaging and I found myself getting slightly concerned that maybe, just maybe, Volley wasn't going to win this after all. I found myself even rooting for Retrofit to win this Battle Royale, but alas an unbiased evaluation of the two had to happen.

The Battle Begins

Volley and Retrofit are easy to use networking frameworks both of which allow for fast implementation and the ability to make asynchronous calls. Both frameworks were designed to accomplish similar goals including the elimination of boilerplate code and the ability to make multiple requests at one time. Due to the similarities of the two frameworks and what they are capable of, the decision between the two had to be made on a deeper level.

Making a Request-Round 1

While working with a large code base, when making such a major change the easier to implement the better. Keeping that in mind, both Volley and Retrofit require very little code to stand up a default request each with their own approach. Volley comes preloaded with a few basic requests including string requests, json requests, and image requests. Retrofit, unlike Volley, comes with one basic request packaged with Gson, which automatically serializes and deserializes to and from a POJO. One must implement a custom request extending the default Request class in order to accomplish this feat within Volley. For the purpose of being on the same playing field I will demonstrate how to implement a generic request within Volley, which will serialize and deserialize any object you pass within it utilizing the Gson library. We will use this request for the remainder of the post.


public class VolleyGsonRequest extends Request {
private final Gson gson = new Gson();
private final Class clazz;
private final Listener listener;
private String mRequestBody;
private static final String PROTOCOL_CHARSET="utf-8";
private Context context;

public VolleyGsonRequest(int method, String url, Object body, Class clazz,
Listener listener, ErrorListener errorListener, Context context){
super(method, url, errorListener);
this.clazz = clazz;
this.listener = listener;
this.context = context;

//convert from pojo to json (not used in this example)
if(body!=null){
mRequestBody = gson.toJson(body);
}
}

/*
getting the body of our request in bytes
*/
@Override
public byte[] getBody(){
try{
return mRequestBody == null? null : mRequestBody.getBytes(PROTOCOL_CHARSET);
}catch(UnsupportedEncodingException e){
VolleyLog.wtf("Unsupported Encoding while trying to get the bytes of %s using %s", mRequestBody, PROTOCOL_CHARSET);
return null;
}
}

@Override
protected void deliverResponse(T response){
listener.onResponse(response);
}

@Override
protected Response 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));
}
}
}

The custom request above is now capable of receiving a POJO and delivering a new one from the response. Now to perform the request we will just need to run the following code.

//initialize request
VolleyGsonRequest volleyRequest = new VolleyGsonRequest((int) Request.Method.GET, url,null,
SearchData.class, new VolleySuccessListener(), new VolleyErrorListener(), this);
//initialize queue
RequestQueue queue = Volley.newRequestQueue(this);
//make the call
queue.add(volleyRequest);

To perform the same request with Retrofit you will first need to add an interface for the service call.

public interface RetrofitSearchService {
@GET("/{search}/&rsz=8")
void getResults(@Path("search") String search, Callback searchCallback);
}

Once the interface is set up we are ready to make our call.

//initialize Retrofit adapter with base url
RestAdapter restAdapter = new RestAdapter.Builder().setEndpoint(Url).build();
//create our service
RetrofitSearchService searchService = restAdapter.create(RetrofitSearchService.class);
//make the call
searchService.getResults("String To Search", new RetroCallback());

Though both frameworks are capable of the same outcome, Retrofit is capable of accomplishing this without customization. However, if you are implementing multiple requests within your application, and I suspect you are, customization may be necessary within Retrofit. Though that could be the case, at the simplest level Retrofit has the easier solution. Round 1: Retrofit.

Custom Headers - Round 2

Being able to customize the headers of a request was very important to us. Since we were not working with synchronous calls anymore we could not implement a singleton HttpClient to reuse and keep a session alive, so we were left to rely on the request headers. Adding custom headers with Volley or Retrofit is a simple task both of which require very little code.

Using the GsonRequest that we created earlier with Volley, we will need to override the getHeaders() method to return or add to the map of headers going to be used.

@Override
public Map getHeaders(){
Map headers = new HashMap,>
headers.put("Content-Type", "Application/x-www-form-url-encoded");
headers.put("Accept", "Application/json"
);
return headers;
} ,string>,>

With Retrofit we will just need to utilize the interface that we created earlier and add the headers using the @Header annotation as shown below.

@Headers({
"Accept: application/json",
"Content-Type: application/x-www-form-urlencoded"
})

Each framework allows for such easy implementation of custom headers, I would be hard pressed to say which one is easier. Round 2: Draw.

Speed - Round 3

How long our users have to wait on the application weighs heavily on a decision such as this one. Speed dramatically affects the user's perspective of the application. That being said, I decided to perform my own tests utilizing the included application. The application leverages a google search which responds with the top 8 results. With a set of 100 different variables to search for I tested both the Volley framework and Retrofit framework independently on the same WiFi network with the same device. My results were inconclusive to say the least, with Volley showing to be 1.1% faster, which breaks down to a 3.42 ms difference. Such a minute difference is not great enough and was not consistent enough for me to make a decision on. In the end, I chose to agree that both Volley and Retrofit were of comparable speed. Round 3: Draw.

Caching - Round 4

Cache control can play a pretty important role when choosing between Retrofit and Volley. Retrofit, unfortunately, does not offer much cache control and what is offered is managed solely through the request headers. This isn't a totally unreasonable, but retrieving a response from a previously completed request is not an option short of making the request again. With Volley, you can retrieve a previous response from the RequestQueue by using the URL as the cache key. With Volley you can also clear the cache for a specific response or the cache for the whole RequestQueue forcing the framework to retrieve a new, updated response at your discretion. Some of Volley's caching abilities are shown below. Round 4: Volley.

//get a cached response and deserialize it
String json = new String(queue.getCache().get(url));
SearchData data = gson.fromJson(json, SearchData.class);

//clear the entire cache
queue.getCache().clear();

//clear only one response in a cache
queue.getCache().remove(url);

Image Requests - Round 5

Being able to make image requests was a pretty large factor in the making of our decision. Volley comes packaged with a loader specifically designed to download images for you. Packaged along with the loader is a custom view called the NetworkImageView in which the developer only has to hand a URL and an ImageLoader to and Volley does the rest. This view is specifically targeted to work well with list views and allow for automatic cancellation of requests when the images parent view is destroyed. On the contrary, Retrofit does not easily support image downloads. To accomplish what we are able to with Volley, one would be required to download and include another library in your project such as Picasso. Round 5: Volley.

General Tools & Features - Round 6

In general there are a few tools and features that weighed in on the decision. One feature is the setting of a request's time out. Such a simple feature is made awkwardly complicated with Retrofit. You would have to pass a new OkHttpClient into the RestAdapter with Retrofit to accomplish what is done in one line of code with Volley.

//timeout in ms, max number of retries, backoff multiplier
volleyRequest.setRetryPolicy(new DefaultRetryPolicy(10000,1,0));

Another simple feature is the ability to cancel a request. With Retrofit, once a request is made you cannot cancel it. Being able to cancel a request is a feature you may not use often, however, it does come in handy in many use cases. With Volley, you also have the ability to set a requests priority. This comes in handy when making multiple requests at one time. Such a feature, though it may not be used often, is not included in Retrofit. Round 6: Volley.

Documentation - Round 7

As it is for most developers, my relationship with documentation is very much a love hate one. I loath the mornings I wake up knowing my day will be filled with nothing but documentation. However, trying to leverage a new library or framework without it can prove to be very challenging, as I learned in my experience with Volley. The lack of a document that centralizes all the information you would need to know about the framework is Volley's Achilles Heel. In fact, when first beginning my use with Volley (before there were so many great blogs and articles on the subject) I found the best source of "documentation" to be the code and the surrounding comments. As you can imagine, this was not very efficient and rather frustrating. Retrofit on the other hand, has a well-formatted searchable document that answers just about any question you might have when leveraging the framework. I was able to download the framework and learn how to use it in just a matter of minutes. Round 7: Retrofit.

Winner

At the end of the battle, we chose to go with Volley. Volley fit well into our already mature code base without requiring too much of a code overhaul when the Spring framework was removed. The ability to customize the framework to our specific needs was a huge plus and it allowed us to leave our existing models/requests alone with the ability to pass them all through a single custom Volley Request. Volley's ImageLoader and image requests were exploited immediately within our application allowing us to eliminate quite a bit of code that was in place to achieve what Volley was able to accomplish in just a few lines. Though Volley won this battle, it is far from winning them all. In fact, through this process I found that Retrofit would have been a better fit (pun intended) with a previous project that I had completed leveraging Volley due to its simplicity and specific niche. Each project is its own and should be treated as such. Volley won at the end of the day for us, but depending on your needs another framework, dare I say one that I didn't mention here, may be the better solution for you. In the meantime, please enjoy the attached application as a sample of what Volley and Retrofit are capable of.