Android Networking I: OkHttp, Volley and Gson

英文链接:https://medium.com/android-news/android-networking-i-okhttp-volley-and-gson-72004efff196#.ogjc1kdf6

Prologue

This is the first article of a series:(后面跟进)

  • Android Networking II: OkHttp, Retrofit, Moshi and Picasso (coming article)
  • Android Networking III: Ion (coming article)
  • Android Networking IV: Android Async Http (coming article)

Assumptions

Motivation

There’s something that probably you can’t avoid in an Android project: Networking. Whether you are loading images, requesting data from an API server or getting a single byte from internet, you are doing networking.

Given how complex and omnipresent networking is on Android, one of the questions Android developers are facing nowadays is which solutions should I use? There are a lot of good libraries out there and you can stack one upon another in different ways.

The root of so many great networking libraries is that the offered options in the Android framework are not great and were a mess to deal with in the old days (Eclair, Froyo and Gingerbread). You had to write a lot of boilerplate code each time you were doing networking and probably you’ll be doing a sub-optimal (a.k.a. poor) job. This was the ideal scenario to solve once for all a really big and important problem and libraries started to appear and evolve.

In the old days networking in Android was a nightmare, nowadays the problem is to find out which solution best fits the project necessities.

In this article we’ll talk about a particular solution: OkHttp, Volley and Gson.

The aim of this article is just share my discoveries and experiences, learn from others and maybe help some people.

OkHttp

OkHttp is an modern, fast and efficient Http client which supports HTTP/2 and SPDY and does a lot of stuff for you. Reading how many things OkHttp does it’s a good way to understand how hard networking is: Connection pooling, gziping, caching, recovers from network problems, sync and async calls, redirects, retries … and so on.

OkHttp is a very capable networking tool out of the box, without the need of any REST library (Retrofit, Volley…) and probably is the library most developers would choose if they could only include one library in their projects.

OkHttp sits on top of Okio, a library that complements java.io and java.nio to make it much easier to access, store, and process your data. It provides fast I/O and resizable buffers.

OkHttp depends Okio, but Okio can be used by its own.

OkHttp is an modern, fast and efficient Http client which supports HTTP/2 and SPDY and sits on top of Okio.

Volley

Volley is a REST client that makes easy common networking tasks. Takes care of requesting, loading, caching, threading, synchronization and some more stuff. It’s ready to deal with JSON, images, caching, raw text and allow some customization.

Volley was design for RPC style network operations that populate the UI. Is good for short operations.

Volley by default uses as transport layer the Apache Http stack on Froyo and HttpURLConnection stack on Gingerbread and above. The reason is there are problems with those http stacks have on different Android versions.

Volley allow us to easily set up OkHttp as its transport layer.

Volley was developed by Google.

This is what Android networking looks like in Ficus Kirkpatrick (a Googler behind Volley) words. A lot of parallel async calls.

Volley takes care of requesting, loading, caching, threading, synchronization and more. It’s ready to deal with JSON, images, caching, raw text and allow some customization.

Gson

Gson is JSON serialization and deserialization library that uses reflection to populate your Java model objects from JSON objects. You can add your own serializers and deserializers as well to better control and customization.

Gson was developed by Google.

Setup

Gradle dependencies in Android Studio

You need to add the next lines to your app’s build.gradle file.

compile 'com.google.code.gson:gson:2.4.0'
compile 'com.squareup.okhttp:okhttp:2.5.0'
compile 'com.mcxiaoke.volley:library:1.0.19'

Note: The versions may be different as they are updated. Try to avoid + syntax on version numbers as is recommended.

It´s not necessary to explicitly include the Okio dependency because OkHttp already has it.

Update: As of SDK 23, apache http client was removed so it´s necessary to include in the next line in the android section of your build.gradle:

useLibrary 'org.apache.http.legacy'

All the dependencies above are official but Volley, that is not official but is trustworthy. There’s not official gradle dependency for Volley as far as I know as I’m writing this.

Volley

The way Volley works is creating requests and adding them to a queue. One queue is enough for the whole application, so each time you want to make a request you’ll get the (only) Volley queue to add the request to that queue.

I’m using a global application singleton instance of the queue with the next method:

/**
 * Returns a Volley request queue for creating network requests
 *
 * @return {@link com.android.volley.RequestQueue}
 */
public RequestQueue getVolleyRequestQueue()
{
   if (mRequestQueue == null)
   {
      mRequestQueue = Volley.newRequestQueue(this, new OkHttpStack(new OkHttpClient()));
   }
   return mRequestQueue;
}

The method we are using to create a new request queue has an HttpStack as a parameter. If you use the method that don’t provide an HttpStack Volley will create an stack depending on your API level. (based on theAndroidHttpClient for API level 9 and and HttpURLConnection stack for API level 10 and above)

As I mention before, we’d like to use OkHttp as our transport layer, that’s the reason we are using as a parameter an OkHttpStack. The OkHttpClient implementation I’m using is this one.

The next are methods to add the requests to the Volley requests queue:

/**
 * Adds a request to the Volley request queue with a given tag
 * 
 * @param request is the request to be added
 * @param tag is the tag identifying the request
 */
public static void addRequest(Request<?> request, String tag)
{
    request.setTag(tag);
    addRequest(request);
}
/**
 * Adds a request to the Volley request queue
 * 
 * @param request is the request to add to the Volley queue
 */
public static void addRequest(Request<?> request)
{
    getInstance().getVolleyRequestQueue().add(request);    
}

And this is the method to cancel requests that should normally used in theonStop lifecycle method.

/**
 * Cancels all the request in the Volley queue for a given tag
 *
 * @param tag associated with the Volley requests to be cancelled
 */
public static void cancelAllRequests(String tag)
{
    if (getInstance().getVolleyRequestQueue() != null)
    {
        getInstance().getVolleyRequestQueue().cancelAll(tag);
    }
}

So far we already have Volley and OkHttp ready. So we can start making either String, JsonObject or JsonArray request.

A JsonObject request would be like this:

JsonObjectRequest jsonObjectRequest =
        new JsonObjectRequest(Request.Method.GET, mUrl, new Response.Listener<JSONObject>()
        {
            @Override
            public void onResponse(JSONObject response)
            {
                // Deal with the JSONObject here
            }
        },
        new Response.ErrorListener()
        {
            @Override
            public void onErrorResponse(VolleyError error)
            {
                // Deal with the error here
            }
        });

App.addRequest(jsonObjectRequest, mTAG);

We still need to parse the JSON object to our Java model. The response we are receiving on every Volley request (either String, JsonObject or JsonArray) is not really useful as it is.

You are not alone in the Android networking world.

Gson

We can customize the request to get as responses Java objects that match our data model and we are more comfortable with. All it’s needed is aGsonRequest class extending the Volley Request like in this example.

In the next example we can how would be a GET call to retrieve and parse a Json object:

/**
 * Returns a dummy object parsed from a Json Object to the success  listener and a Volley error to the error listener
 *
 * @param listener is the listener for the success response
 * @param errorListener is the listener for the error response
 *
 * @return @return {@link com.sottocorp.sotti.okhttpvolleygsonsample.api.GsonGetRequest}
 */
public static GsonRequest<DummyObject> getDummyObject
(
        Response.Listener<DummyObject> listener,
        Response.ErrorListener errorListener
)
{
    final String url = "http://www.mocky.io/v2/55973508b0e9e4a71a02f05f";

    final Gson gson = new GsonBuilder()
            .registerTypeAdapter(DummyObject.class, new DummyObjectDeserializer())
            .create();

    return new GsonRequest<>
            (
                    url,
                    new TypeToken<DummyObject>() {}.getType(),
                    gson,
                    listener,
                    errorListener
            );
}

In the next example we can how would be a GET call to retrieve and parse a Json array:

/**
 * Returns a dummy object's array in the success listener and a Volley error in the error listener
 *
 * @param listener is the listener for the success response
 * @param errorListener is the listener for the error response
 *
 * @return @return {@link com.sottocorp.sotti.okhttpvolleygsonsample.api.GsonGetRequest}
 */
public static GsonRequest<ArrayList<DummyObject>> getDummyObjectArray
(
        Response.Listener<ArrayList<DummyObject>> listener,
        Response.ErrorListener errorListener
)
{
    final String url = "http://www.mocky.io/v2/5597d86a6344715505576725";

    final Gson gson = new GsonBuilder()
            .registerTypeAdapter(DummyObject.class, new DummyObjectDeserializer())
            .create();

    return new GsonRequest<>
            (
                    url,
                    new TypeToken<ArrayList<DummyObject>>() {}.getType(),
                    gson,
                    listener,
                    errorListener
            );
}

The Gson parsing with a GsonRequest happens on the background worker thread instead of the main thread.

I’m providing a deserializer in the examples above but note that is not mandatory to provide serializers/deserializers and Gson can handle this very well as far as the field names in the class match (including the case) with the names in the JSON file. I like to provide my serializer/deserializer for customization.

On both examples above we are making GET calls. In case the call is a POST one I’ve included an for a GsonPostRequest and how to use it.

OkHttp works as the transport layer for Volley, which on top of OkHttp is a handy way of making network requests that are parsed to Java objects by Gson just before delivering the response to the main thread

Loading images

Image loading in Android is common and complex. Threading, requesting, transformations, memory management, caches…Probably we could right an entire article about it. The good news is there are out there so many good libraries that most of us will never realize how hard it is.

ImageLoader and NetworkImageView

Volley has a custom view called NetworkImageView (subclassingImageView) that is very handy to load images. You can set an URL, a default view holder and an error image.

Example:

mNetworkImageView = (NetworkImageView) itemView.findViewById(R.id.networkImageView);
mNetworkImageView.setDefaultImageResId(R.drawable.ic_sun_smile);
mNetworkImageView.setErrorImageResId(R.drawable.ic_cloud_sad);
mNetworkImageView.setImageUrl(imageUrl, App.getInstance().getVolleyImageLoader());

The important bit in the code above is the setImageUrl method, which receives two parameters: the image address and an ImageLoader (Volleyhelper that handles loading and caching images from remote URLs)

Let’s take a look at the getVolleyImageLoader method and how we can get an ImageLoader.

/**
 * Returns an image loader instance to be used with Volley.
 *
 * @return {@link com.android.volley.toolbox.ImageLoader}
 */
public ImageLoader getVolleyImageLoader()
{
    if (mImageLoader == null)
    {
        mImageLoader = new ImageLoader
                (
                        getVolleyRequestQueue(),
                        App.getInstance().getVolleyImageCache()
                );
    }

    return mImageLoader;
}

/**
 * Returns a bitmap cache to use with volley.
 *
 * @return {@link LruBitmapCache}
 */
private LruBitmapCache getVolleyImageCache()
{
    if (mLruBitmapCache == null)
    {
        mLruBitmapCache = new LruBitmapCache(mInstance);
    }
    return mLruBitmapCache;
}

The only piece missing in this puzzle is LruBitmapCache. Volley does not provide us with an implementation but we can get one from here that looks appropriate and handles the cache size per device specs, which is cool.

ImageRequest

In some cases we might want not to use NetworkImageView. Image for example we want circular images and we are using CircleImageView. In that case we’ll have to use ImageRequest, which works like this:

final CircleImageView circleImageView =
            (CircleImageView) findViewById(R.id.circularImageView);

    // Retrieves an image specified by the URL, displays it in the UI.
    final com.android.volley.toolbox.ImageRequest imageRequest =
            new ImageRequest
            (
                    mImageUrl,
                    new Response.Listener<Bitmap>()
                    {
                        @Override
                        public void onResponse(Bitmap bitmap)
                        {
                            circleImageView.setImageBitmap(bitmap);
                        }
                    },
                    0,
                    0,
                    ImageView.ScaleType.CENTER_INSIDE,
                    null,
                    new Response.ErrorListener()
                    {
                        public void onErrorResponse(VolleyError error)
                        {          circleImageView.setImageResource(R.drawable.ic_cloud_sad);
                        }
                    }
            );
    // Access the RequestQueue through your singleton class.
    App.getInstance().getVolleyRequestQueue().add(imageRequest);
}

Interesting facts

  • All of the components we’ve talk about in this article (Okio, OkHttp, Volley and Gson) can be used as a standalone. They don’t need each other except OkHttp needing Okio.
  • One of the first articles I linked in the introduction (this one) was written by Jesse Wilson. Jesse Wilson is one of the guys behind Android’s HTTP, Gson, OkHttp and Okio. I thought that deserves a mention and my acknowledgement.
  • OkHttp engine is backing HttpURLConnection as of Android 4.4. Twitter, Facebook and Snapchat bundles it as well.
  • Volley was born as a solution to be used in the Google Play Store inside Google.
  • Both OkHttp and Okio are developed by the Square guys.

Final thoughts

The Volley/Gson solution is mature and was quite popular around 2013 and 2014 mainly for been a Google solution and appear in the Android Developers website. Is still a good option to use because it just works well and it´s fast.

On the other hand, the documentation was always poor (still nowadays) and they are not being actively developed anymore (and for a while).

Resources

Show me the Code