# Android Networking : Retrofit at a Glance

> If get to know something new by reading my articles, don't forget to endorse me on [LinkedIn](https://www.linkedin.com/in/rommansabbir/)

In the realm of Android development, **Retrofit** has emerged as a go-to library for handling **API communication**. Its simplicity, flexibility, and powerful features make it an ideal choice for interacting with web services. In this blog post, we will dive deep into Retrofit and explore how to use it for different **HTTP** methods, including **GET, POST, PUT, PATCH, and DELETE**. Additionally, we'll discover the concept of mock responses and learn how to use them with Retrofit. All examples will be written in **Kotlin**.

To get started with Retrofit, add the following dependency to your project's `build.gradle` file:

```kotlin
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
```

To enable support for `Gson` serialization/deserialization also includes the `Gson` converter dependency:

```kotlin
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
```

## Various `HTTP` methods supported by **Retrofit**

* **GET Request**: A `GET` request s used to retrieve data from a server. Example of how to perform a `GET` request using **Retrofit**:
    

```kotlin
interface UserApiService {
    @GET("users/{id}")
    suspend fun getUser(@Path("id") userId: String): Response<User>
}

val retrofit = Retrofit.Builder()
    .baseUrl(BASE_URL)
    .addConverterFactory(GsonConverterFactory.create())
    .build()

val apiService = retrofit.create(UserApiService::class.java)

// Making the GET request
val response = apiService.getUser(999)
if (response.isSuccessful) {
    val user = response.body()
    // Handle the user data
} else {
    // Handle the error
}
```

* **POST Request**: A `POST` request is used to send data to a server. Example of how to perform a `POST` request using **Retrofit**:
    

```kotlin
interface UserApiService{
    @POST("users")
    suspend fun createUser(@Body user: User): Response<User>
}

val user = User("Romman Sabbir", "rommansabbir@gmail.com")

// Making the POST request
val response = apiService.createUser(user)
if (response.isSuccessful) {
    val createdUser = response.body()
    // Handle the created user data
} else {
    // Handle the error
}
```

* **PUT Request**: A `PUT` request is used to update existing resources on the server. Example of how to perform a `PUT` request using **Retrofit**:
    

```kotlin
interface UserApiService{
    @PUT("users/{id}")
    suspend fun updateUser(@Path("id") userId: String, @Body updatedUser: User): Response<User>
}

val updatedUser = User("Romman Sabbir", "rommansabbir@gmail.com")

// Making the PUT request
val response = apiService.updateUser(999, updatedUser)
if (response.isSuccessful) {
    val updatedUser = response.body()
    // Handle the updated user data
} else {
    // Handle the error
}
```

* **PATCH** Request: A `PATCH` request is similar to a `PUT` request, but it is used to perform partial updates on resources. Example of how to perform a `PATCH` request using **Retrofit**:
    

```kotlin
interface UserApiService{
    @PATCH("users/{id}")
    suspend fun updateUser(@Path("id") userId: String, @Body updatedFields: Map<String, String>): Response<User>
}

val updatedFields = mapOf("email" to "rommansabbir@gmail..com")

// Making the PATCH request
val response = apiService.updateUser(999, updatedFields)
if (response.isSuccessful) {
    val updatedUser = response.body()
    // Handle the updated user data
} else {
    // Handle the error
}
```

* **DELETE** Request: A `DELETE` request is used to delete resources from the server. Here's an example of how to perform a `DELETE` request using **Retrofit**:
    

```kotlin
interface UserApiService{
    @DELETE("users/{id}")
    suspend fun deleteUser(@Path("id") userId: String): Response<Unit>
}

// Making the DELETE request
val response = apiService.deleteUser(999)
if (response.isSuccessful) {
    // Handle the successful deletion
} else {
    // Handle the error
}
```

---

## Different Kinds of POST Method w/ Retrofit

Retrofit supports various types of **POST** methods to send data to a server. Let's explore different types of **POST** methods and how to implement them using **Kotlin** with **Retrofit**.

* **Form URL-encoded POST request**: Form URL-encoded requests are commonly used when sending data as key-value pairs, similar to how HTML forms are submitted. Here's an example of how to implement a form URL-encoded POST request using Retrofit:
    

**Define the API service interface**:

```kotlin
interface ApiService {
    @FormUrlEncoded
    @POST("endpoint")
    fun sendData(
        @Field("param1") param1: String,
        @Field("param2") param2: String
    ): Call<ResponseBody>
}
```

**Make the POST request**:

```kotlin
val call = apiService.sendData("value1", "value2")
call.enqueue(object : Callback<ResponseBody> {
    override fun onResponse(call: Call<ResponseBody>, response: Response<ResponseBody>) {
        if (response.isSuccessful) {
            // Request successful
        } else {
            // Handle error
        }
    }

    override fun onFailure(call: Call<ResponseBody>, t: Throwable) {
        // Handle failure
    }
})
```

* JSON POST request: JSON POST requests are commonly used when sending complex data structures or objects as JSON payloads. Here's an example of how to implement a JSON POST request using Retrofit:
    

**Define the API service interface**:

```kotlin
interface ApiService {
    @Headers("Content-Type: application/json")
    @POST("endpoint")
    fun sendData(@Body requestBody: RequestBody): Call<ResponseBody>
}
```

We specify the `"Content-Type"` header as `"application/json"` using the `@Headers` annotation. The request body is represented by the `@Body` annotation, and we pass a `RequestBody` object.

**Make the POST request**:

```kotlin
val requestBody = RequestBody.create(MediaType.parse("application/json"), json)
val call = apiService.sendData(requestBody)
call.enqueue(object : Callback<ResponseBody> {
    override fun onResponse(call: Call<ResponseBody>, response: Response<ResponseBody>) {
        if (response.isSuccessful) {
            // Request successful
        } else {
            // Handle error
        }
    }

    override fun onFailure(call: Call<ResponseBody>, t: Throwable) {
        // Handle failure
    }
})
```

* Multipart POST request (file upload): Multipart requests are used when uploading files to a server. We will explore Multipart POST below.
    

---

## Post Image, Audio, Video w/ Retrofit

* Create an interface that defines the API endpoints for uploading media files. Assume you have a RESTful API with an endpoint `/upload` that accepts a media file. Create a new Kotlin file, e.g., `MediaService.kt`, and define the service interface as follows:
    

```kotlin
interface MediaService {
    @Multipart
    @POST("/upload")
    fun uploadMedia(
        @Part("image\"; filename=\"image.jpg\"") image: RequestBody?,
        @Part("audio\"; filename=\"audio.mp3\"") audio: RequestBody?,
        @Part("video\"; filename=\"video.mp4\"") video: RequestBody?
    ): Call<ResponseBody>
}
```

We have a single API endpoint `/upload` that accepts three different media files: `image`, `audio`, and `video`. Each file is represented by a `RequestBody` object annotated with the `@Part` annotation. The `filename` parameter specifies the desired filename for each file.

* Prepare the media files for upload
    

Before uploading the media files, we need to prepare them for upload. Assuming you have the file paths or URIs for each file, you can create `RequestBody` objects from them. Here's an example of how to do it:

```kotlin
val imageFile = File(imageFilePath)
val imageRequestBody = imageFile.asRequestBody("image/*".toMediaTypeOrNull())

val audioFile = File(audioFilePath)
val audioRequestBody = audioFile.asRequestBody("audio/*".toMediaTypeOrNull())

val videoFile = File(videoFilePath)
val videoRequestBody = videoFile.asRequestBody("video/*".toMediaTypeOrNull())
```

`imageFilePath`, `audioFilePath`, and `videoFilePath` represent the paths to the respective media files. We create `RequestBody` objects using the `asRequestBody` extension function, specifying the media type based on the file extension.

* Upload the media files
    

Now that everything is set up, you can upload the media files using the Retrofit service interface. Make the API call by invoking the `uploadMedia` method and passing the respective `RequestBody` objects as parameters:

```kotlin
val call = mediaService.uploadMedia(imageRequestBody, audioRequestBody, videoRequestBody)
call.enqueue(object : Callback<ResponseBody> {
    override fun onResponse(call: Call<ResponseBody>, response: Response<ResponseBody>) {
        if (response.isSuccessful) {
            // Media upload successful
        } else {
            // Handle error
        }
    }

    override fun onFailure(call: Call<ResponseBody>, t: Throwable) {
        // Handle failure
    }
})
```

---

## Post File w/ Retrofit

* Create a service interface defining the API endpoints for file upload:
    

```kotlin
interface FileUploadService {
    @Multipart
    @POST("upload")
    suspend fun uploadFile(
        @Part file: MultipartBody.Part
    ): Response<YourResponseModel>
}
```

* Create the `RequestBody` for your file:
    

```kotlin
val file = File("path_to_your_file")
val requestFile = RequestBody.create(MediaType.parse("multipart/form-data"), file)
val body = MultipartBody.Part.createFormData("file", file.name, requestFile)
```

* Make the API call to upload the file:
    

```kotlin
try {
    val response = service.uploadFile(body)
    if (response.isSuccessful) {
        // Handle success
    } else {
        // Handle error
    }
} catch (e: Exception) {
    // Handle exception
}
```

---

## File Upload Progress

Add `OkHttp` dependency:

```kotlin
implementation("com.squareup.okhttp3:okhttp:4.10.0")
```

To track the progress of the file upload, we can use an `OkHttpClient` instance with a custom `Interceptor` that implements the `ProgressListener` interface. This listener receives progress updates during the upload process. Here's an example of how we can implement it:

```kotlin
class ProgressInterceptor(private val progressListener: ProgressListener) : Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        val originalRequest = chain.request()
        val requestBuilder = originalRequest.newBuilder()

        val requestBody = originalRequest.body
        if (requestBody != null) {
            val progressRequestBody = ProgressRequestBody(requestBody, progressListener)
            requestBuilder.method(originalRequest.method, progressRequestBody)
        }

        val request = requestBuilder.build()
        return chain.proceed(request)
    }
}

interface ProgressListener {
    fun onProgress(progress: Int)
}
```

The `ProgressInterceptor` intercepts the network request and wraps the request body with a `ProgressRequestBody`. The `ProgressListener` interface provides a callback to track the progress of the upload.

To use the `ProgressInterceptor`, modify the creation of the `OkHttpClient` instance as follows:

```kotlin
val progressInterceptor = ProgressInterceptor(object : ProgressListener {
    override fun onProgress(progress: Int) {
        // Update the progress here
    }
})

val httpClient = OkHttpClient.Builder()
    .addInterceptor(progressInterceptor)
    .build()

val retrofit = Retrofit.Builder()
    .baseUrl("http://example.com/")
    .client(httpClient)
    .build()
```

The `ProgressInterceptor` is added to the `OkHttpClient` instance using the `addInterceptor` method. This allows the interceptor to track the progress of the file upload. The `onProgress` method in the `ProgressListener` interface is called with the progress value during the upload process, allowing us to update the progress as needed.

---

## Using Mock Responses in Retrofit w/ MockWebServer

**Mock responses** are incredibly useful during testing and development, allowing you to simulate server responses without relying on an actual backend. Retrofit provides the `MockWebServer` class for creating a local `HTTP` server that responds with pre-defined mock responses. Here's how you can use mock responses with each `HTTP` method in **Retrofit**:

* Mock `GET` Request:
    

```kotlin
val server = MockWebServer()
server.enqueue(MockResponse().setBody("This is a simple mock response."))

// Start the server and make the request
server.start()
val baseUrl = server.url("/")
val retrofit = Retrofit.Builder()
    .baseUrl(baseUrl)
    .addConverterFactory(GsonConverterFactory.create())
    .build()

// Define the API service
interface MockUserApiService {
    @GET("data")
    suspend fun getData(): Response<String>
}

val apiService = retrofit.create(MockUserApiService::class.java)

// Make the GET request
val response = apiService.getData()
if (response.isSuccessful) {
    val responseData = response.body()
    // Handle the mock response data
} else {
    // Handle the error
}

// Shutdown the server
server.shutdown()
```

* Mock `POST`, `PUT`, `PATCH`, and `DELETE` Requests: Mocking `POST`, `PUT`, `PATCH`, and `DELETE` requests follow a similar pattern. You need to define the respective annotations in the **API service interface** and enqueue the desired mock response in the `MockWebServer`.
    

```kotlin
// Similar steps as the GET request, just replace the annotation in the API service interface
interface MockApiService {
    @POST("data")
    suspend fun postData(@Body requestBody: RequestBody): Response<String>
    // Other mock methods here
}
```

---

## Mock Responses in Retrofit from Android Assets

Mock responses are invaluable during testing and development, enabling you to simulate server responses without relying on a live backend. One approach is to load mock responses from the Android assets folder. Here's how you can incorporate mock responses using Retrofit:

* Create a folder named `mock_responses` in the `assets` directory of your Android project.
    
* Save the mock response files as JSON files in the `mock_responses` folder. For example, `get_user_success.json` might contain the mock response for the GET request to retrieve a user's data.
    
* Implement a custom `Interceptor` that intercepts the HTTP requests and returns the corresponding mock response if it matches the request's URL.
    

```kotlin
class MockInterceptor(private val context: Context) : Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        val request = chain.request()
        val response = getMockResponse(request.url.toString())
        return response ?: chain.proceed(request)
    }

    private fun getMockResponse(requestUrl: String): Response? {
        val path = requestUrl.substring(BASE_URL.length) // Adjust as per your base URL structure
        val fileName = "mock_responses/$path.json"
        val inputStream = context.assets.open(fileName)
        val source = inputStream.source().buffer()
        val mockResponse = Response.Builder()
            .code(200)
            .protocol(Protocol.HTTP_1_1)
            .message("Mock response")
            .body(ResponseBody.create("application/json".toMediaTypeOrNull(), source.readString(Charsets.UTF_8)))
            .build()
        inputStream.close()
        return mockResponse
    }
}
```

* Add the `MockInterceptor` to your Retrofit instance when building it:
    

```kotlin
val interceptor = MockInterceptor(context) // Replace `context` with your application context
val httpClient = OkHttpClient.Builder()
    .addInterceptor(interceptor)
    .build()

val retrofit = Retrofit.Builder()
    .baseUrl(BASE_URL)
    .client(httpClient)
    .addConverterFactory(GsonConverterFactory.create())
    .build()

// Use the Retrofit instance as usual
```

### **Note:**

* Add `MockInterceptor` only when your application is in `DEBUG` Mode.
    
* To simulate different kinds of methods calling for a single \`**ENDPOINT\`** (ex: `update_user_info`) use `chain.request.method()` (ex: `if (chain.request.method() == "get"`) or `if (chain.request.method() == "post"`) or others (`PUT`, `PATCH`)
    

---

That's it for today. Happy coding...
