Creating your own HTTP API wrapper library: Retrofit 2

Tompee Balauag
Familiar Android
Published in
5 min readMay 22, 2018
Photo by michael podger on Unsplash

Note: This is the first part of the creating your own HTTP API wrapper library series.

Network communication has always been the bread and butter of android applications. It allows you to build social apps and connect users everywhere, push and receive data from a server, execute financial transactions and even stream videos and audio.

Creating a network module in an android application is a very tedious task. You would have to create a lot of components such as an HTTP client and a response handler and parser. Do not forget error handling and threading (network calls are not allowed on the main thread).

Back in my early days as an android developer, I usually implement my own network module using native APIs for a higher degree of customization. It requires a lot of planning and development time, let alone debugging. Along the way, I discovered other libraries that simplifies most of the nuances of networking. Such libraries include Volley and Okhttp. In this tutorial, we will be learning about retrofit 2. It is an HTTP client library that uses OkHttp in the backend by default and converts the response to Java objects using a multitude of converters.

We will start by including the artifact in our project. Note that retrofit requires at least Java 7 and Android 2.3.

implementation 'com.squareup.retrofit2:retrofit:2.4.0'

I always see Retrofit 2 as similar in approach to Dagger 2. You create a service interface and Retrofit will implement it for you. I think this is a good approach as it offloads implementation from user and fits easily in an architecture perspective.

Note: Dagger 2 tutorial here.

For this example, we will use a dummy REST API server. Our server has different endpoints so it would be good for demonstration purposes.

ApiService

Our first component will be the ApiService. It will contain the interface to our server. Let’s take a look at an example.

An API service is an interface. This is because retrofit will implement this for us. Your API service should contain all the endpoints that you wish to reach from your server. Let’s break down the code above.

@GET("posts")
fun getPosts(): Call<List<Post>>
// corresponding endpoint: /posts

The @GET annotation corresponds to the HTTP request methods. In retrofit, there are 5 built in methods, GET, POST, PUT, DELETE and HEAD. The method annotation maps the endpoint to the annotated function. Endpoint can also be customized.

@GET("posts/{postId}")
fun getPostById(@Path("postId") postId: Int): Call<Post>
// corresponding sample endpoint: /posts/1

The code above has a dynamic path. To specify a dynamic path, enclose the variable with {} and annotate a parameter with @Path with the same variable name.

@GET("comments")    
fun getCommentByPostId(@Query("postId") postId: Int): Call<List<Post>>
// corresponding sample endpoint: /comments?postId=1

Queries are also supported. Just annotate a parameter with @Query and supply the query string. Query parameters are eventually converted to strings.

Notice the return type. Return types in Retrofit 2 should be enclosed in a Call type. This is a method invocation object. This will allow you to execute the operation synchronously or asynchronously. More on that later.

Lastly, pay attention to the actual return type. It is an object of type Post. This will be our model. This will represent the response as a plain old object. Therefore it should correspond with the actual response structure. Before we define it, let’s take a look first at converters.

Converter

Converters are serializers that can convert a particular response format to a an object. Retrofit supports a lot of converters for formats such JSON and XML, among others. You can also create your own custom converter but that is out of this tutorial’s scope. It is up to you to determine the differences and advantages of different converters but in this tutorial we will be using the Jackson converter. To include the artifact, add this to the gradle file.

implementation 'com.squareup.retrofit2:converter-jackson:2.4.0'

Model

Now that we have included our converter, we can now use it’s annotations for our model. Consider the return of the posts/1 endpoint.

{
"userId": 1,
"id": 1,
"title": "sunt aut facere repellat ...",
"body": "quia et suscipit\nsuscipit ..."
}

The response contains four elements, userId of type integer, id of type integer, title of type string and body of type body. Let us define these for elements in our Post class.

When using Jackson, creating models can be very straightforward. You can just create a data class with parameter names equal to the json properties. The example above describe it. The JsonIgnoreProperties annotation instructs the converter to ignore unknown properties. This is useful when you want to ignore some elements. Also, 1 important rule when creating converter models. Models should have an empty constructor, hence the default values in the constructor.

In cases wherein the variable name does not equal the property name, you can use the @JsonProperty annotation. However, there is a slight nuance when using it in Kotlin. For some reason, when 1 item is annotated with @JsonProperty in constructor, it is required to annotate everything else.

Connecting the dots

Let us now put our service to the test. To create a service, we need the base URL.

First create an instance of a Retrofit.Builder and add your desired converter. In our case, we provide a JacksonConverter. Add the base URL and build to create a retrofit instance. Using the retrofit instance, we can create our ApiService by invoking create.

Synchronous vs. Asynchronous

Requests can be invoked synchronously or asynchronously. The Call object provides 2 methods for just those, execute and enqueue. Let’s take a look first at execute.

Execute runs on the same thread as the one used to invoke it. Let us try running a request in the main thread.

Running the above code will net us an app crash. Yes. NetworkOnMainThreadException.

Now let us try the asynchronous approach. The method for that is enqueue. Enqueue requires a callback object that has 2 methods, onFailure and onResponse. In enqueue, the request runs in a separate thread and the callback is executed by default on the main thread.

Running the above code will not trigger a NetworkOnMainThreadException as expected.

Bonus: RxJava integration

RxJava 2 can be integrated in Retrofit as well. You have to include the call adapter factory artifact for starters.

implementation 'com.squareup.retrofit2:adapter-rxjava2:2.4.0'

Then in the retrofit builder, add an instance of the RxJava2CallAdapterFactory.

val retrofit = Retrofit.Builder()
.addConverterFactory(JacksonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.baseUrl("https://jsonplaceholder.typicode.com/")
.build()

Then you can now modify your return types as observables.

@GET("posts/{postId}")
fun getPostById(@Path("postId") postId: Int): Observable<Post>

That’s it for the Retrofit primer. We will use this when we discuss an approach in creating your own HTTP wrapper API. So watch out for the 2nd part of this series.

--

--