Android Networking : Retrofit at a Glance
Unleashing the Power of Retrofit for Seamless Communication and Enhanced User Experience

Senior Android Engineer from Bangladesh. Love to contribute in Open-Source. Indie Music Producer.
If get to know something new by reading my articles, don't forget to endorse me on LinkedIn
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:
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
To enable support for Gson serialization/deserialization also includes the Gson converter dependency:
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
Various HTTP methods supported by Retrofit
- GET Request: A
GETrequest s used to retrieve data from a server. Example of how to perform aGETrequest using Retrofit:
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
POSTrequest is used to send data to a server. Example of how to perform aPOSTrequest using Retrofit:
interface UserApiService{
@POST("users")
suspend fun createUser(@Body user: User): Response<User>
}
val user = User("Romman Sabbir", "[email protected]")
// 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
PUTrequest is used to update existing resources on the server. Example of how to perform aPUTrequest using Retrofit:
interface UserApiService{
@PUT("users/{id}")
suspend fun updateUser(@Path("id") userId: String, @Body updatedUser: User): Response<User>
}
val updatedUser = User("Romman Sabbir", "[email protected]")
// 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
PATCHrequest is similar to aPUTrequest, but it is used to perform partial updates on resources. Example of how to perform aPATCHrequest using Retrofit:
interface UserApiService{
@PATCH("users/{id}")
suspend fun updateUser(@Path("id") userId: String, @Body updatedFields: Map<String, String>): Response<User>
}
val updatedFields = mapOf("email" to "[email protected]")
// 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
DELETErequest is used to delete resources from the server. Here's an example of how to perform aDELETErequest using Retrofit:
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:
interface ApiService {
@FormUrlEncoded
@POST("endpoint")
fun sendData(
@Field("param1") param1: String,
@Field("param2") param2: String
): Call<ResponseBody>
}
Make the POST request:
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:
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:
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
/uploadthat accepts a media file. Create a new Kotlin file, e.g.,MediaService.kt, and define the service interface as follows:
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:
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:
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:
interface FileUploadService {
@Multipart
@POST("upload")
suspend fun uploadFile(
@Part file: MultipartBody.Part
): Response<YourResponseModel>
}
- Create the
RequestBodyfor your file:
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:
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:
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:
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:
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
GETRequest:
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, andDELETERequests: MockingPOST,PUT,PATCH, andDELETErequests follow a similar pattern. You need to define the respective annotations in the API service interface and enqueue the desired mock response in theMockWebServer.
// 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_responsesin theassetsdirectory of your Android project.Save the mock response files as JSON files in the
mock_responsesfolder. For example,get_user_success.jsonmight contain the mock response for the GET request to retrieve a user's data.Implement a custom
Interceptorthat intercepts the HTTP requests and returns the corresponding mock response if it matches the request's URL.
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
MockInterceptorto your Retrofit instance when building it:
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
MockInterceptoronly when your application is inDEBUGMode.To simulate different kinds of methods calling for a single `ENDPOINT` (ex:
update_user_info) usechain.request.method()(ex:if (chain.request.method() == "get") orif (chain.request.method() == "post") or others (PUT,PATCH)
That's it for today. Happy coding...




