Retrofit networking library for Android

Share

REST services

A large number of todays most popular applications on Android and other mobile platforms are usually created to show their users some type of content. Most users even do not pay much attention to interface elements or advanced options but are exclusively interested to see new images, interesting videos, or exchange few words and emoticons with their friends or people they follow. Also this content has to be diverse and fresh or our users will quickly move to other applications.

It is quite obvious that this behavior can not be supported by using the classic desktop application model where we could pack some business logic, user interface and some kind of local database along with the application. Much better approach here would be to put the database on some central server and let users access it using some kind of service. If we add to this the wish of users to create and modify content and make it available to other users or to run some kind of operations on their data we can abstract away from pure database and call these services a resource which users have access to. Thus we logically get to RESTful approach.
There is much more to REST then just exposing some resources over HTTP and serving them in JSON format. But even if we accept this simplification and don’t get into more details we can still do a lot in our mobile applications since almost every big company today offers some kid of REST API for us to use. Even if we do not want to use 3rd party services there is still a lot of value in making our applications using this architecture since we can make our systems easier to develop, test and maintain.

REST on Android the hard way

So how do we access this wast world of REST services and follow good practices in our Android applications?

  • First we need some sort of network library or class to communicate over HTTP. Android framework has on offer URLConnection
  • Next we are going to need something to handle the JSON serialization/deserialization to keep things simple we can use GSON
  • Let’s not forget we must not do long running operations (including network communication) on the Main Thread. We can for example use Android AsyncTask

So if we pack this into some class we get this:

      private class GetExamplesOperation extends AsyncTask<Example, Void, String> {

    @Override
    protected Example doInBackground(String... params) {
        getExamples(params[0]);
    }

    @Override
    protected void onPostExecute(Example example) {
        //Do something with example here probably by calling a callback passed into AsyncTask constructor
    }

    public static Example getExamples(String examplesUrl) {
        HttpURLConnection urlConnection = null;
        try {
            // create connection
            URL urlToRequest = new URL(serviceUrl);
            urlConnection = (HttpURLConnection) urlToRequest.openConnection();
            urlConnection.setConnectTimeout(CONNECTION_TIMEOUT);
            urlConnection.setReadTimeout(DATARETRIEVAL_TIMEOUT);

            // handle errors
            int statusCode = urlConnection.getResponseCode();
            if (statusCode == HttpURLConnection.HTTP_UNAUTHORIZED) {
                // handle authorization
            } else if (statusCode != HttpURLConnection.HTTP_OK) {
                // handle 404, 500, etc
            }

                // create JSON object from content
            InputStream in = new BufferedInputStream(urlConnection.getInputStream());
            Gson gson = new GsonBuilder().create();
            Example example = gson.fromJson(reader, Example.class);;
            return example;
        } catch (MalformedURLException e) {
            // URL is invalid
        } catch (SocketTimeoutException e) {
            // data retrieval or connection timed out
        } catch (IOException e) {
            // (could not create input stream)
        } catch (JSONException e) {
            // response body is no valid JSON string
        } finally {
            if (urlConnection != null) {
                urlConnection.disconnect();
        }
        }       

        return null;
    }
    }   

This looks quite verbose for something we could do in Bash with curl using a single line.
Also this code is greatly simplified and we would probably be calling a lot more endpoints and thus duplicate this many times. It would also be nice to cache the responses and pool connections to save on resources.
Let’s not forget the fact that there are also a lot of hidden pitfalls when using AsyncTasks (like life-cycle issues, canceling, different thread pooling behaviors on different version etc) and that URLConnection has some specifics and even bugs when used on different version of Android. To avoid this we could write more code but in best case we would waste a lot of time implementing/testing and in the worst case create bugs and maintainability problems only to do something as simple as getting some data from a service.

Retrofit to the rescue

Problems described above do seem like something many developers would face considering the popularity of REST services. That was indeed the case for good people at Square so they created their solution to it and made i publicly available as an Open source project (along with many other useful libraries) under the name Retrofit. This library is quite popular with Android developers due to its ease of use, good documentation, great performance and large number of questions and answers on Stack Overflow. It is also free to use and licensed under Apache License which is very friendly even to commercial usage.

To use it in our project since it is available on JCenter we just need to add a dependency line in our build.gradle

      compile 'com.squareup.retrofit2:retrofit:2.2.0'

After this the library follows one simple rule. We define interfaces representing the service we are communicating with and define one method for each URL method.
For example to get all Examples from the service named ExampleRest for specific user we write the code like this:

      public interface ExampleRest {
    @GET("{user}/examples")
    Call<List<Example>> getExamples(@Path("user") String username);
}

Note the smartly named annotations used to indicate:

  • The request method used (@GET)
  • Part of the URL path that is changing based on the username (@Path)

So to change a GET to POST (other supported methods are PUT, DELETE and HEAD) we just annotate a method differently and Retrofit does the rest.

You might be asking but that is just an Interface and how do we implement it? And the smart part about the library is that we do not need to do that. We just call:

      Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("http://example.com/v2")
    .build();
ExampleRest exampleRest = retrofit.create(ExampleRest.class);

And using “magic” (and reflection) the library does the implementation for us. From then on we just use the created object by calling its methods and the networking and serialization/deserialization is done by the library.

     Call<List<Example>> callExamples = exampleRest.getExamples("test");
List<Example> examples = callExamples.execute();

Do note that this code is synchronous and should not be called from the UI thread. We could move this into an AsyncTask or create a background thread and synchronize but Retrofit can help us with this also.

      Call<List<Example>> callExamples = exampleRest.getExamples("test");
call.enqueue(new Callback<List<Example>>() {  
    @Override
    public void onResponse(Call<List<Example>> call, Response<List<Example>> response) {
        if (response.isSuccessful()) {
            // Everything was successful list of Examples is available inside the response object
        } else {
            // error response
        }
    }
    @Override
    public void onFailure(Call<List<Example>> call, Throwable t) {
        Log.d("Error", t.getMessage());
        Toast.makeText(context,t.getMessage(), Toast.LENGTH_SHORT).show();
    }
}

Callback Interface methods are quite explanatory, we just need to handle the success and error cases. There is also a call to Toast.makeText(…) in onFailure without posting to the Main Thread looper. This is possible because Retrofit will by default execute callback methods on the main thread on Android.

What if we wanted to call our Example API with specific example id? Without Retrofit we would have to resort to concatenating Strings or using Uri.Builder with its verbose syntax. But instead we can use URL manipulation annotations.

      public interface ExampleRest {
    @GET("examples/{id}")
    Call<List<Example>> getExampleById(@Path("id") int exampleId, @Query("sort") String sort);
}

Changing of paths is done by adding alphanumeric value inside {} and using @Path annotation to pass in this parameter into a method. This {} is not limited to end of URL but can be put in any part of it.
@Query is used to add query string to URL, we can also use a type safe @QueryMap Map<String, int> params to add multiple parameters.

Sending data is also quite simple, we just need to define a method for POST request with our data in the body as:

      @FormUrlEncoded
@POST("examples")
Call<User> updateExample(@Field("display_name") String name, @Field("date") String date);

or

     @Multipart
@POST("examples")
Call<User> updateExample(@Field("thumbnail") RequestBody photo);

@FormUrlEncoded should be used to send small amount of text data, like some key/value pairs user would input into a form in the browser.
@Multipart should be used to send binary files since encoding them with Url encoding would produce a large amount of overhead due to replacing of non-alphanumeric characters. To send a file in this way we need to put it inside of MultipartBody.Part. We could also use other object types then RequestBody if our types are serializable by Retrofit converter used (by default this is GSON).

By default Retrofit is setup with sensible defaults and in most cases there is no need to modify its settings. If such need arises configuration is done using the chained Builder pattern like this:

      final OkHttpClient okHttpClient = new OkHttpClient.Builder()
    .writeTimeout(60, TimeUnit.SECONDS)
    .readTimeout(60, TimeUnit.SECONDS)
    .connectTimeout(60, TimeUnit.SECONDS)
    .build();

Retrofit retrofit = new Retrofit.Builder()
    .addConverterFactory(GsonConverterFactory.create())
    .client(okHttpClient)
    .baseUrl("http://example.com/v2")
    .build();

Retrofit is made in a modular fashion so we can switch the client which is used for HTTP operations. By default this is OkHttp which is another great Square open source library.
Also serialization/deserialization logic is delegated to special classes called Converters for which we can define our own or use one of many provided:

JSON:

  • Gson: com.squareup.retrofit:converter-gson
  • Jackson: com.squareup.retrofit:converter-jackson
  • Moshi: com.squareup.retrofit:converter-moshi

Protocol Buffers:

  • Protobuf: com.squareup.retrofit:converter-protobuf
  • Wire: com.squareup.retrofit:converter-wire

XML:

  • Simple XML: com.squareup.retrofit:converter-simplexml

Java strings and primitives and their boxed types

  • Scalars Converter: com.squareup.retrofit2:converter-scalars:latest.version

Conclusion

Since networking on Android can be quite complex and error prone and we usually want to invest development time into writing something original and not boiler plate code, using third party libraries to do some of the work can be quite helpful. Al-thou not by any means the only choice Retrofit is probably the most widely used, battle tested and supported library currently used on Android to simplify communication with Rest services.
And did I mention you can also have all of this in your Java applications that run on JVM.

Share

Sign in to get new blogs and news first:

Leave a Reply

Igor Čordaš

Senior Android Developer @Endava
mm

Interested to see, learn and create solutions on Android platform from phones to fridges.

Active member of IT comunity in the field of Java technologies, Data Science, Machine Learning, Internet of things and Game Development.

Curently working on developing system applications for one of the leading mobile phone producers.

Sign in to get new blogs and news first.

Categories