Retrofit 2 — Creating a Sustainable Android Client

Retrofit offers a wide range of functionalities and there are a lot of possible configurations. A lot of larger applications will require some specific setup, for example for OAuth authentication. In order to achieve a clean and stable project, we'll introduce you our idea of a sustainable Android client: the ServiceGenerator.

Retrofit Series Overview

  1. Introduction to Call Adapters (Coming soon)
  1. Callbacks (Coming soon)
  2. Annotations (Coming soon)
  3. Fluent Interface with Builders (Coming soon)

The ServiceGenerator

As you know from our getting started with Retrofit tutorial, the Retrofit object and its builder are the heart of all requests. Here you configure and prepare your requests, responses, authentication, logging and error handling. Unfortunately, we've seen too many developers just copy-and-pasting these parts instead of separating into one clean class. The ServiceGenerator will give you our solution, which is based on Bart Kiers' idea.

Let's start with the simple code. In its current state, it only defines one method to create a basic REST client for a given class/interface, which returns a service class from the interface.

public class ServiceGenerator {

    private static final String BASE_URL = "https://api.github.com/";

    private static Retrofit.Builder builder =
            new Retrofit.Builder()
                    .baseUrl(BASE_URL)
                    .addConverterFactory(GsonConverterFactory.create());

    private static Retrofit retrofit = builder.build();

    private static OkHttpClient.Builder httpClient =
            new OkHttpClient.Builder();

    public static <S> S createService(
        Class<S> serviceClass) {
        return retrofit.create(serviceClass);
    }
}

The ServiceGenerator class uses Retrofit’s Retrofit builder to create a new REST client with the given API base url (BASE_URL). For example, GitHub’s API base url is located at https://api.github.com/ and you must update the provided example url with your own url to call your API instead of GitHub’s.

The createService method takes a serviceClass, which is the annotated interface for API requests, as a parameter and creates a usable client from it. On the resulting client you'll be able to execute your network requests.

Why Is Everything Declared Static Within the ServiceGenerator?

You might wonder why we use static fields and methods within the ServiceGenerator class. Actually, it has one simple reason: we want to use the same objects (OkHttpClient, Retrofit, …) throughout the app to just open one socket connection that handles all the request and responses including caching and many more. It’s common practice to just use one OkHttpClient instance to reuse open socket connections. That means, we either need to inject the OkHttpClient to this class via dependency injection or use a static field. As you can see, we chose to use the static field. And because we use the OkHttpClient throughout this class, we need to make all fields and methods static.

Additionally to speeding things up, we can save a little bit of valuable memory on mobile devices when we don't have to recreate the same objects over and over again.

Using the ServiceGenerator

Remember how our code looked in the getting started with Retrofit tutorial?

String API_BASE_URL = "https://api.github.com/";

OkHttpClient.Builder httpClient = new OkHttpClient.Builder();

Retrofit.Builder builder =  
    new Retrofit.Builder()
            .baseUrl(API_BASE_URL)
            .addConverterFactory(
                GsonConverterFactory.create()
            );

Retrofit retrofit =  
    builder
        .client(
            httpClient.build()
        )
        .build();

GitHubClient client = retrofit.create(GitHubClient.class);  

For one request, this looks fine. But if you have dozens of network requests throughout your app, it'll be a nightmare to manage. With our ServiceGenerator, you only need a single line:

GitHubClient client = ServiceGenerator.createService(GitHubClient.class);  

All of the preparations were moved into our ServiceGenerator.

Unfortunately, in most cases the ServiceGenerator cannot stay this simple. Thus, the code from above only gives you a starting point. You'll need to adapt it to your needs just like we'll do in other tutorials. Nevertheless, in the next two sections we'll explore a few possible changes.

Preparing Logging

One of the most common wishes for developers is to know what kind of data is actually being send and received by Retrofit. We have an entire tutorial dedicated on logging with Retrofit where you can learn more.

Logging with Retrofit 2 is done by an interceptor called HttpLoggingInterceptor. You'll need to add an instance of this interceptor to the OkHttpClient. For example, you could solve it the following way:

public class ServiceGenerator {

    private static final String BASE_URL = "https://api.github.com/";

    private static Retrofit.Builder builder =
            new Retrofit.Builder()
                    .baseUrl(BASE_URL)
                    .addConverterFactory(GsonConverterFactory.create());

    private static Retrofit retrofit = builder.build();

    private static HttpLoggingInterceptor logging =
            new HttpLoggingInterceptor()
                    .setLevel(HttpLoggingInterceptor.Level.BODY);

    private static OkHttpClient.Builder httpClient =
            new OkHttpClient.Builder();

    public static <S> S createService(
        Class<S> serviceClass) {
        if (!httpClient.interceptors().contains(logging)) {
            httpClient.addInterceptor(logging);
            builder.client(httpClient.build());
            retrofit = builder.build();
        }

        return retrofit.create(serviceClass);
    }
}

There are a few things you've to be aware of. Firstly, make sure you're not accidentally adding the interceptor multiple times! We check with httpClient.interceptors().contains(logging) if the logging interceptor is already present. Secondly, make sure to not build the retrofit object on every createService call. Otherwise the entire purpose of the ServiceGenerator is defeated.

Prepare Authentication

The requirements for authentication are a little bit different. You can learn more in our tutorials on Basic Authentication, Token Authentication, OAuth, or even Hawk Authentication. While the details are a little different for each authentication implementation, you probably will have to change the ServiceGenerator. One of the changes is that you need to pass additional parameters to createService to create a client.

Let's look at an example for Hawk authentication:

public class ServiceGenerator {

    private static final String BASE_URL = "https://api.github.com/";

    private static Retrofit.Builder builder =
            new Retrofit.Builder()
                    .baseUrl(BASE_URL)
                    .addConverterFactory(GsonConverterFactory.create());

    private static Retrofit retrofit = builder.build();

    private static HttpLoggingInterceptor logging =
            new HttpLoggingInterceptor()
                    .setLevel(HttpLoggingInterceptor.Level.BODY);

    private static OkHttpClient.Builder httpClient =
            new OkHttpClient.Builder();

    public static <S> S createService(
            Class<S> serviceClass, final HawkCredentials credentials) {
        if (credentials != null) {
            HawkAuthenticationInterceptor interceptor =
                    new HawkAuthenticationInterceptor(credentials);

            if (!httpClient.interceptors().contains(interceptor)) {
                httpClient.addInterceptor(interceptor);

                builder.client(httpClient.build());
                retrofit = builder.build();
            }
        }

        return retrofit.create(serviceClass);
    }
}

Our createService now has a second parameter for the HawkCredentials. If you pass a non-null value, it'll create the necessary Hawk authentication interceptor and add it to the Retrofit client. We also need to rebuild Retrofit to apply our changes to the next request.

One more heads-up, you probably will see slightly different versions of the ServiceGenerator in the other tutorials. Don't be confused! We recommend that you also keep your ServiceGenerator slim and specialized for the use case!

What Comes Next

In this tutorial you've learned why centralizing your Retrofit client generation makes sense and is recommended. You've seen one approach how you can implement it with the ServiceGenerator class. Nevertheless, you probably will have to adjust it to your purposes.

If you've feedback or a question, let us know in the comments or on twitter @futurestud_io.

Make it rock & enjoy coding!


Explore the Library

Find interesting tutorials and solutions for your problems.

Miscellaneous