Retrofit 2 for API calls

Retrofit by Square is one of the most famous and widely used REST clients for Android. It internally uses OkHTTP for HTTP and network calls. The word REST client makes it different from other networking libraries in Android. While most of the networking libraries (Volley, OkHTTP, and others) focus on synchronous/asynchronous requests, prioritization, ordered requests, concurrent/parallel requests, caching, and more. Retrofit gives more attention to making network calls and parsing data more like method calls. It simply turns your HTTP API into a Java interface. And it doesn't even try to solve network problems by itself, but delegates this to OkHTTP internally.

So, how does it transform an HTTP API into a Java interfaces? Retrofit simply uses a converter to serialize/deserialize POJO (plain old Java object) classes into/from JSON or XML. Now, what is a converter? Converters are those helper classes that parse JSON/XML for you. A converter generally uses the Serializable interface internally to convert to/from JSON/XML and POJO classes (data classes in Kotlin). It being pluggable gives you many choices of converters, such as the following:

  • Gson
  • Jackson
  • Guava
  • Moshi
  • Java 8 converter
  • Wire
  • Protobuf
  • SimpleXML

We will use Gson for our book. To work with Retrofit, you'll need the following three classes:

  • A Model class (POJO or data class)
  • A class to provide you with the Retrofit client instance with the help of Retrofit.Builder()
  • An Interface that defines possible HTTP operations, including the request type (GET or POST), parameters/request body/query strings, and finally the response type

So, let's get started with the Model class.

Before creating the class, we need to know the structure of the JSON response first. We all saw JSON responses in the previous chapter, but, as a quick recap, here is the JSON response for the GET_TODO_LIST API:

    { 
      "error_code": 0, 
      "error_message": "", 
      "data": [ 
       { 
         "id": 1, 
         "todoDescription": "Lorem ipsum dolor sit amet, consectetur 
adipiscing elit. Integer tincidunt quis lorem id rhoncus. Sed
tristique arcu non sapien consequat commodo. Nulla dolor
tellus, molestie nec ipsum at, eleifend bibendum quam.", "todoTargetDate": "2017/11/18", "status": "complete" } ] }

The error_code denotes whether there are any errors. If error_code is a non-zero value, then there must be an error. If it's zero, then there is no error, and you can proceed with parsing the data.

The error_message will contain information for you if there's an error. If the error_code is zero, the error_message will be blank.

The data key will hold a JSON array for the list of todos.

One thing to note here is that error_code and error_message will be consistent for all APIs in our project, so it will be better if we create a base class for all the APIs, and then we extend that class for each API when required.

This is our BaseAPIResponse class:

    open class BaseAPIResponse ( 
      @SerializedName("error_code") 
      val errorCode:Int, 
      @SerializedName("error_message") 
      val errorMessage:String): Serializable 

We have two val properties in this class—errorCode and errorMessage; note the annotations @SerializedName. This annotation is used by Gson to declare the serialized name for a property; the serialized name should be the same as the JSON response. You can easily avoid this annotation if you have the same variable name as the JSON response. If the variable name is different, the serialized name is used to match the JSON response.

Let's now move ahead with GetToDoListAPIResponse; the following is the class definition:

    open class GetToDoListAPIResponse( 
      errorCode:Int, 
      errorMessage:String, 
      val data:ArrayList<ToDoModel> 
    ):BaseAPIResponse(errorCode,errorMessage) 

Here, we skipped the @Serialized annotation for data, as we are using the same name as the JSON response. The remaining two properties are declared by the BaseAPIResponse class.

For data, we are using an ArrayList of ToDoModel; Gson will take care of the rest to convert a JSON array to an ArrayList.

Let's now take a look at the ToDoModel class:

    data class ToDoModel ( 
      val id:Int, 
      var todoDescription:String, 
      var todoTargetDate:String, 
      var status:String 
    ):Serializable 

The builder class for Retrofit is simple, as shown here:

    class APIClient { 
      private var retrofit: Retrofit? = null 
      fun getClient(): Retrofit { 
         
        if(null == retrofit) { 
 
          val client = OkHttpClient.Builder().connectTimeout(3,
TimeUnit.MINUTES) .writeTimeout(3, TimeUnit.MINUTES) .readTimeout(3,
TimeUnit.MINUTES).addInterceptor(interceptor).build() retrofit = Retrofit.Builder() .baseUrl(Constants.BASE_URL) .addConverterFactory(GsonConverterFactory.create()) .client(client) .build() } return retrofit!! } fun getAPIService() =
getClient().create(APIService::class.java) }

The getClient() function is responsible to create and provide you with a Retrofit client. The getAPIService() function helps you with pairing the Retrofit client with your defined HTTP operations and create an instance of the interface.

We used OkHttpClient and Retrofit.Builder() to create the Retrofit instance. If you're not familiar with them, you may visit http://www.vogella.com/tutorials/Retrofit/article.html.

Let's now create the interface for the HTTP operations—APIService—as follows:

    interface APIService { 
      @POST(Constants.GET_TODO_LIST) 
      fun getToDoList(): Call<GetToDoListAPIResponse> 
 
      @FormUrlEncoded 
      @POST(Constants.EDIT_TODO) 
      fun editTodo( 
            @Field("todo_id") todoID:String, 
            @Field("todo") todo:String 
      ): Call<BaseAPIResponse> 
 
      @FormUrlEncoded 
      @POST(Constants.ADD_TODO) 
      fun addTodo(@Field("newtodo") todo:String): Call<BaseAPIResponse> 
     } 

We have created API interfaces for all our APIs. Note the return types of the functions. They return a Call instance that encapsulates the actual expected response.

Now, what is Call instance? And what is the purpose of using it?

The Call instance is an invocation of a Retrofit method that sends a request to a webserver and returns a response. Each call yields its own HTTP request and response pair. What to do with the Call<T> instance? We have to enqueue it with a Callback<T> instance.

So, the same pull mechanism, same callback hell. However, we should be reactive, shouldn't we? Let's do that.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.118.141.27