Consuming APIs with Retrofit

Overview

Retrofit is a type-safe REST client for Android, Java and Kotlin developed by Square. The library provides a powerful framework for authenticating and interacting with APIs and sending network requests with OkHttp. See this guide to understand how OkHttp works.

This library makes downloading JSON or XML data from a web API fairly straightforward. Once the data is downloaded then it is parsed into a Plain Old Java Object (POJO) which must be defined for each "resource" in the response.

Setup

Make sure to require Internet permissions in your AndroidManifest.xml file:

Note: if you are upgrading from Retrofit 2 beta 1 or beta2, your package imports will need to be changed from import retrofit.XXXX to import retrofit2.XXXX. You will also need to update your OkHttp imports from import okhttp.XXXX to import okhttp3.XXXX because of the new OkHttp3 dependency:

In the past, Retrofit relied on the Gson library to serialize and deserialize JSON data. Retrofit 2 now supports many different parsers for processing network response data, including Moshi, a library build by Square for efficient JSON parsing. However, there are a few limitations, so if you are not sure which one to choose, use the Gson converter for now.

Converter

Library

Gson

com.squareup.retrofit2:converter-gson:2.4.0

Jackson

com.squareup.retrofit2:converter-jackson:2.4.0

Moshi

com.squareup.retrofit2:converter-moshi:2.4.0

Protobuf

com.squareup.retrofit2:converter-protobuf:2.4.0

Wire

com.squareup.retrofit2:converter-wire:2.4.0

Simple XML

com.squareup.retrofit2:converter-simplexml:2.4.0

Create Java Classes for Resources

There are two approaches discussed in this guide. The first way is the manual approach, which requires you to learn how to use the Gson library. The second approach is you can also auto-generate the Java classes you need by capturing the JSON output and using jsonschema2pojo. We encourage you to follow the manual way to best understand how the auto-generated code approach works.

Manual approach - Creating Java classes by hand

See this guide about leveraging the Gson library for more information about how to create your own Java classes for use with Retrofit. The guide shows how to use Gson to ingest data from the Rotten Tomatoes API, but it can be used in the same way for any RESTful web service.

Automated approach - Auto-generating the Java classes

Assuming you have the JSON response already, go to jsonschema2pojo.
Make sure to select JSON as the Source Type:

Set the Annotation style to Gson.

Next, paste the JSON output into the textbox:

Click the Preview button. You should see the top section look sort of like the following:

Paste the generated class into your project under a models sub-package. Rename the class name Example to reflect your model name. For this example, we will call this file and class the User model.

Note: Android does not come normally with many of the javax.annotation library by default. If you wish to keep the @Generated annotation, you will need to add this dependency. See this Stack Overflow discussion for more context. Otherwise, you can delete that annotation and use the rest of the generated code.

dependencies{provided'org.glassfish:javax.annotation:10.0-b28'}

Creating the Retrofit instance

To send out network requests to an API, we need to use the Retrofit builder class and specify the base URL for the service.

Note also that we need to specify a factory for deserializing the response using the Gson library. The order in which the converters are added will be the sequence in which they are attempted to be processed as discussed in this video talk. If we wish to pass in a custom Gson parser instance, it can be specified too:

Define the Endpoints

With Retrofit 2, endpoints are defined inside of an interface using special retrofit annotations to encode details about the parameters and request method. In addition, the return value is always a parameterized Call<T> object such as Call<User>. If you do not need any type-specific response, you can specify return value as simply Call<ResponseBody>.

For instance, the interface defines each endpoint in the following way:

publicinterfaceMyApiEndpointInterface{// Request method and URL specified in the annotation
@GET("users/{username}")Call<User>getUser(@Path("username")Stringusername);@GET("group/{id}/users")Call<List<User>>groupList(@Path("id")intgroupId,@Query("sort")Stringsort);@POST("users/new")Call<User>createUser(@BodyUseruser);}

Notice that each endpoint specifies an annotation of the HTTP method (GET, POST, etc.) and method that will be used to dispatch the network call. Note that the parameters of this method can also have special annotations:

Annotation

Description

@Path

variable substitution for the API endpoint (i.e. username will be swapped for {username} in the URL endpoint).

@Query

specifies the query key name with the value of the annotated parameter.

@Body

payload for the POST call (serialized from a Java object to a JSON string)

@Header

specifies the header with the value of the annotated parameter

Changing the base URL

Normally, the base URL is defined when you instantiated an Retrofit instance. Retrofit 2 allows you to override the base URL specified by changing it in the annotation (i.e. if you need to keep one specific endpoint using an older API endpoint)

@POST("https://api.github.com/api/v3")

There are also others that allow you to modify the base URL using relative paths (and not the fully qualified URL) as discussed in this blog article.

Adding headers

Notice that there is a @Headers and @Header annotation. The Headers can be used to provide pre-defined ones:

If you need to specify a unique filename for your multipart upload, there is currently an issue in Retrofit 2 tracked in this ticket. Alternatively, you can create a multi-part RequestBody according to this OkHttp recipe guide and pass it along as one of the @Part annotated parameters:

POSTing JSON data

Retrofit 2 will use the converter library chosen to handle the deserialization of data from a Java object. If you annotate the parameter with a @Body parameter, this work will be done automatically. If you are using the Gson library for instance, any field belonging to the class will be serialized for you. You can change this name using the @SerializedName decorator:

Upgrading from Retrofit 1

If you are trying to upgrade from Retrofit 1, you may remember that the last parameter had to be a Callback type if you wanted to define the API call to run asynchronously instead of synchronously:

publicinterfaceMyApiEndpointInterface{// Request method and URL specified in the annotation
// Callback for the parsed response is the last parameter
@GET("users/{username}")voidgetUser(@Path("username")Stringusername,Callback<User>cb);@GET("group/{id}/users")voidgroupList(@Path("id")intgroupId,@Query("sort")Stringsort,Callback<List<User>>cb);@POST("users/new")voidcreateUser(@BodyUseruser,Callback<User>cb);}

Retrofit 1 relied on this Callback type as a last parameter to determine whether the API call would be dispatched asynchronously instead of synchronously. To avoid having two different calling patterns, this interface was consolidated in Retrofit 2. You now simply define the return value with a parameterized Call<T>, as shown in the previous section.

Accessing the API

We can bring this all together by constructing a service leveraging the MyApiEndpointInterface interface with the defined endpoints:

Shown above, Retrofit will download and parse the API data on a background thread, and then deliver the results back to the UI thread via the onResponse or onFailure method.

Note also that OkHttp, which dispatches the callback on the worker thread, callbacks in Retrofit are dispatched on the main thread. Because UI updates can only be done on the main thread, the approach used by Retrofit can make it easier to make changes to your views.

If you are using Retrofit in a Background Service instead of an Activity or Fragment, you can run the network call synchronously within the same thread by using the execute() method.

publicinterfaceMyApiEndpointInterface{// Request method and URL specified in the annotation
// Callback for the parsed response is the last parameter
@GET("/users/{username}")Observable<User>getUser(@Path("username")Stringusername);@GET("/group/{id}/users")Observable<List<User>>groupList(@Path("id")intgroupId,@Query("sort")Stringsort);@POST("/users/new")Observable<User>createUser(@BodyUseruser);}

Consistent with the RxJava framework, we need to create a subscriber to handle the response. The methods onCompleted(), onError(), and onNext() need to be added. Using the Android RxJava library, we can also designate that we will handle this event on the UI main thread. Note: If you intend to override the default network call behavior, you can specify subscribeOn(). Otherwise, it can omitted.
Note: As the RxJava rewrote their API, the term "Subscription" used here shall be replaced with "Disposable". As the word "Subscription" had conflict intentions.

Stringusername="sarahjean";Observable<User>call=apiService.getUser(username);Disposabledisposable=call.subscribeOn(Schedulers.io())// optional if you do not wish to override the default behavior
.observeOn(AndroidSchedulers.mainThread()).subscribeWith(newDisposable<User>(){@OverridepublicvoidonCompleted(){}@OverridepublicvoidonError(Throwablee){// cast to retrofit.HttpException to get the response code
if(einstanceofHttpException){HttpExceptionresponse=(HttpException)e;intcode=response.code();}}@OverridepublicvoidonNext(Useruser){}});

Note that if you are running any API calls in an activity or fragment, you will need to unsubscribe on the onDestroy() method. The reasons are explained in this Wiki article:

Note also that subscribing an observer to the observable is what triggers the network request. For more information about how to attach multiple observers before dispatching the network requests, see this section.

Retrofit and Authentication

Using Authentication Headers

Headers can be added to a request using an Interceptor. To send requests to an authenticated API, add headers to your requests using an interceptor as outlined below:

// Define the interceptor, add authentication headers
Interceptorinterceptor=newInterceptor(){@Overridepublicokhttp3.Responseintercept(Chainchain)throwsIOException{RequestnewRequest=chain.request().newBuilder().addHeader("User-Agent","Retrofit-Sample-App").build();returnchain.proceed(newRequest);}};// Add the interceptor to OkHttpClient
OkHttpClient.Builderbuilder=newOkHttpClient.Builder();builder.interceptors().add(interceptor);OkHttpClientclient=builder.build();// Set the custom client when building adapter
Retrofitretrofit=newRetrofit.Builder().baseUrl("https://api.github.com").addConverterFactory(GsonConverterFactory.create()).client(client).build();

Notice that in Retrofit 2 the interceptor has to be added to a custom OkHttpClient. In Retrofit 1, it could be set directly by the builder class.

Using OAuth

In order to authenticate with OAuth, we need to sign each network request sent out with a special header that embeds the access token for the user that is obtained during the OAuth process. The actual OAuth process needs to be completed with a third-party library such as signpost and then the access token needs to be added to the header using a request interceptor. Relevant links for Retrofit and authentication below:

Issues

There is a known issue currently with Retrofit 2 passing Lint tests tracked in this ticket. In particular, you may see package reference in library; not included in Android: java.nio.file. Referenced from okio.Okio. or Invalid package reference in library; not included in Android: java.lang.invoke. Referenced from retrofit.Platform.Java8..

The lint errors should be suppressed and not trigger any additional errors for now.

Troubleshooting

Retrofit and OkHttp can be hard to troubleshoot when trying to step through the various layers of abstraction in the libraries. You can add the HttpLogInterceptor that can be added when using the OkHttp3 library, which will print HTTP requests/responses through LogCat. You can also leverage Facebook's Stetho project to use Chrome to inspect all network traffic.

HttpLogInterceptor

To use HttpLogInterceptor, add this dependency to your Gradle configuration:

implementation'com.squareup.okhttp3:logging-interceptor:3.3.0'

You will need to add a network interceptor for HttpLogInterceptor. See this doc for the different options that can be used.

OkHttpClient.Builderbuilder=newOkHttpClient.Builder();HttpLoggingInterceptorhttpLoggingInterceptor=newHttpLoggingInterceptor();// Can be Level.BASIC, Level.HEADERS, or Level.BODY
// See http://square.github.io/okhttp/3.x/logging-interceptor/ to see the options.
httpLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);builder.networkInterceptors().add(httpLoggingInterceptor);builder.build();

You then need to pass this custom OkHttpClient into the Retrofit builder:

Retrofitretrofit=newRetrofit.Builder().client(okHttpClient)

Stetho

Facebook's Stetho project enables you to use Chrome debugging tools to troubleshoot network traffic, database files, and view layouts. See this guide for more details.