TheMovieDb API – Part 2 – Setting up Retrofit

Programming in Android Studio 2.0

Welcome back to implementing TheMovieDb API. As always remember all the code for this series will be in this GitHub repo today we are covering the popular type safe http client for Android called Retrofit. Retrofit allows us to turn out HTTP APIs into Java interfaces easy.

Now the first thing we need to make sure is that, since we are connecting to a web service, is that we have the INTERNET permission under our AndroidManifest.xml go ahead a place it like this:

<uses-permission android:name="android.permission.INTERNET" />

Once you have that cover let’s start by creating an ApiClient class that will serve as our “service generator” for the various endpoint options available from the API. Under app/src/main/java/your.package/ create a new class and place the following:

public class ApiClient {


    /**
     * The movie database base api endpoint.
     */
    public final static String API_BASE_ENDPOINT = "https://api.themoviedb.org/3/";

    /**
     * API key query parameter name.
     */
    public static final String PARAM_API_KEY = "api_key";

    private static OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
    private static Retrofit.Builder apiBuilder = new Retrofit.Builder()
            .baseUrl(API_BASE_ENDPOINT)
            .addConverterFactory(GsonConverterFactory.create());


    /**
     * Return the current {@link retrofit2.Retrofit.Builder} instance with the API Key within it's header.
     * <p/>
     */
    public static <S> S createService(Class<S> serviceClass) {
        httpClient.addInterceptor(new Interceptor() {
            @Override
            public Response intercept(Interceptor.Chain chain) throws IOException {
                Request original = chain.request();
                HttpUrl url = original.url().newBuilder()
                        .addQueryParameter(PARAM_API_KEY, BuildConfig.THEMOVIEDB_KEY)
                .build();

                Request request = chain.request().newBuilder().url(url).build();
                return chain.proceed(request);
            }
        });

        OkHttpClient client = httpClient.build();
        Retrofit retrofit = apiBuilder.client(client).build();
        return retrofit.create(serviceClass);
    }
}

Now what did I just did you asked?

First we placed under a constant our TheMovieDb API endpoint url, this helps us unify it under a single place and Retrofit can re-use it.

    /**
     * The movie database base api endpoint.
     */
    public final static String API_BASE_ENDPOINT = "https://api.themoviedb.org/3/";

Second since we need to include our api key with every request we do  we created another constant to hold the parameter, this way if it ever changes on TheMovieDb we can easily change in a single place.

/**
     * API key query parameter name.
     */
    public static final String PARAM_API_KEY = "api_key";

Third we create both a static httpClient and Retrofit.Builder this helps us easily have them load into memory during runtime already.

  private static OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
    private static Retrofit.Builder apiBuilder = new Retrofit.Builder()
            .baseUrl(API_BASE_ENDPOINT)
            .addConverterFactory(GsonConverterFactory.create());

Finally the magic method to do it all, createService, this method will take a Class<S> and return an instance of the class specified to be used, in other words it takes an interface that follows the Retrofit annotations and brings it to life. We also add an interceptor to make sure we include the api key parameter with every call made.

/**
     * Return the current {@link retrofit2.Retrofit.Builder} instance with the API Key within its header.
     * <p/>
     */
    public static <S> S createService(Class<S> serviceClass) {
        httpClient.addInterceptor(new Interceptor() {
            @Override
            public Response intercept(Interceptor.Chain chain) throws IOException {
                Request original = chain.request();
                HttpUrl url = original.url().newBuilder()
                        .addQueryParameter(PARAM_API_KEY, BuildConfig.THEMOVIEDB_KEY)
                .build();

                Request request = chain.request().newBuilder().url(url).build();
                return chain.proceed(request);
            }
        });

        OkHttpClient client = httpClient.build();
        Retrofit retrofit = apiBuilder.client(client).build();
        return retrofit.create(serviceClass);
    }

Now that we have set the basics needed to use Retrofit with TheMovieDb let’s create some domain objects for fetching. When you call an endpoint you get in it’s response usually three things, varies from endpoints to endpoint, a page number of where you are currently at within; total_pages self explanatory and total_results also self explanatory. We need to have a class that defines this properties so when we call our “movie service” retrofit knowns how to bind everything together without us moving a finger. Create a class call BaseModel and place the following:

public class BaseModel {
    public Integer page;
    public Integer total_pages;
    public Integer total_results;
}

Good, now lets create our Movie class, in order to find out the properties you can check the documentation. Add as many as you may need. I will add the following:

public Integer id;
public Boolean adult;
public String title;
public String overview;
public Double popularity;
public Double vote_average;
public String poster_path;
public String backdrop_path;
public Date release_date;
public Integer vote_count;
public Boolean video;
public List<Integer> genre_ids; //ids are now the only thing returned by a Discover call.
public String original_title;

See how easy is to work with Android and Retrofit? Now let’s create one final class called MovieResultsPage which will be in charge of returning a list of movies (again retrofit makes the binding)

/**
 * Holds the Json response objects.
 */
public List<Movie> results;

Great! Now we only need an interface to join things up. Go ahead and create an interface with the following, I decided to name it MovieServiceInterface:

@GET("movie/now_playing")
Call<MovieResultsPage> nowPlaying(@Query("page") Integer page);

Easy right? Now we have our api client class, our initial domain classes and even an interface but still a long way to go, how can we check it works and that it even fetches? Unit testing once again to the rescue!

Under app/src/main/test/java/your.package/ and your test class place the following:

@Test
public void testTheMovieDbEndPointServiceWithRetrofit() {

    try {
        MovieServiceInterface moviesService = ApiClient.createService(MovieServiceInterface.class);
        moviesService.nowPlaying(0).enqueue(new Callback<MovieResultsPage>() {
            @Override
            public void onResponse(Call<MovieResultsPage> call, Response<MovieResultsPage> response) {
                Assert.assertTrue(response.isSuccessful());
                Assert.assertNotNull(response.body().results);
            }

            @Override
            public void onFailure(Call<MovieResultsPage> call, Throwable t) {

            }
        });
    } catch (Exception e) {
        e.printStackTrace();
    }
}

Awesome run it!

Retrofit unit test success
Retrofit unit test success

See why test driven development is so good? We did not had to wait for our views to be ready to know that our endpoint is working and fetching movies. But there is still a long road to cover but don’t worry I’m here with you! Until next time.

Joel

Please follow and like us:
Facebook
Twitter
Joel Sosa

Author: Joel Sosa

Android nanodegree holder | Graduate Student @GeorgiaTech CS Interactive Intelligence | @gdgpuertorico Organizer