π‘ Retrofit Guide
π Retrofit in Android: A Complete Guide
Retrofit is one of the most popular HTTP clients for Android, developed by Square. It simplifies the process of consuming RESTful web services and makes networking in Android apps more modular, readable, and testable.
This blog will dive deep into:
- What is Retrofit?
- How Retrofit works internally
- Setting up Retrofit
- Making GET, POST, PUT, DELETE requests
- Serialization with Gson/Moshi
- Interceptors and Logging
- Error handling
- Uploading files
- Using Coroutines and Kotlin Flow with Retrofit
π What is Retrofit?
Retrofit is a type-safe HTTP client for Android and Java. It allows you to define your REST API as a set of interfaces using annotations and handles the implementation behind the scenes.
Key Features:
- Type-safe HTTP client
- Support for annotations to describe HTTP methods
- Automatic JSON parsing using Gson, Moshi, or other converters
- Support for Coroutines, RxJava, and Kotlin Flow
- Built-in support for file uploads, form-encoded and multipart data
βοΈ How Does Retrofit Work?
- Define an API Interface using Retrofit annotations like
@GET
,@POST
, etc. - Retrofit uses dynamic proxy creation and annotation parsing to convert these method calls into actual HTTP requests.
- Retrofit delegates the HTTP request to a converter (like Gson) to parse the response into the defined data class.
- You get the final parsed data in a callback, suspend function, Flow, or Rx stream depending on your setup.
π Setting Up Retrofit
Add the following dependencies in your build.gradle
:
1
2
3
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0' // For JSON parsing
implementation 'com.squareup.okhttp3:logging-interceptor:4.9.3' // Optional logging
Create the Retrofit Instance
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
object RetrofitClient {
private const val BASE_URL = "https://api.example.com/"
private val loggingInterceptor = HttpLoggingInterceptor().apply {
level = HttpLoggingInterceptor.Level.BODY
}
private val okHttpClient = OkHttpClient.Builder()
.addInterceptor(loggingInterceptor)
.build()
val instance: Retrofit by lazy {
Retrofit.Builder()
.baseUrl(BASE_URL)
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create())
.build()
}
}
π§Ύ Defining API Interfaces
Example: GET Request
1
2
3
4
interface ApiService {
@GET("users")
suspend fun getUsers(): Response<List<User>>
}
Example: POST Request
1
2
@POST("users")
suspend fun createUser(@Body user: User): Response<User>
Example: PUT Request
1
2
@PUT("users/{id}")
suspend fun updateUser(@Path("id") id: Int, @Body user: User): Response<User>
Example: DELETE Request
1
2
@DELETE("users/{id}")
suspend fun deleteUser(@Path("id") id: Int): Response<Unit>
π§ Serialization with Gson or Moshi
Retrofit supports multiple converters. Gson is the most popular.
1
2
3
4
5
data class User(
val id: Int,
val name: String,
val email: String
)
You can also use @SerializedName("json_key")
if your JSON keys differ from variable names.
To use Moshi instead of Gson:
1
implementation 'com.squareup.retrofit2:converter-moshi:2.9.0'
π Logging with Interceptors
Retrofit uses OkHttp under the hood. To log requests/responses:
1
2
3
4
5
6
val logging = HttpLoggingInterceptor()
logging.level = HttpLoggingInterceptor.Level.BODY
val client = OkHttpClient.Builder()
.addInterceptor(logging)
.build()
β οΈ Error Handling
Use Response<T>
to inspect HTTP status codes and error bodies:
1
2
3
4
5
6
7
val response = apiService.getUsers()
if (response.isSuccessful) {
val users = response.body()
} else {
val errorBody = response.errorBody()?.string()
// Handle error
}
For more granular handling, create a sealed class for network results.
π Uploading Files
1
2
3
4
5
@Multipart
@POST("upload")
suspend fun uploadFile(
@Part file: MultipartBody.Part
): Response<UploadResponse>
Preparing the file:
1
2
3
val file = File("path_to_file")
val requestFile = file.asRequestBody("image/*".toMediaTypeOrNull())
val body = MultipartBody.Part.createFormData("file", file.name, requestFile)
π Retrofit with Coroutines and Flow
Using suspend
Functions
Retrofit works seamlessly with coroutines:
1
2
@GET("users")
suspend fun getUsers(): Response<List<User>>
Returning Flow
You can wrap Retrofit calls inside flow
blocks to support reactive streams:
1
2
3
4
5
6
7
8
fun getUsersFlow(): Flow<List<User>> = flow {
val response = api.getUsers()
if (response.isSuccessful) {
emit(response.body() ?: emptyList())
} else {
throw Exception("Error: ${response.code()}")
}
}
π§ͺ Testing Retrofit
You can easily mock ApiService
in unit tests using libraries like Mockito
or MockWebServer
(also by Square).
1
2
3
val mockResponse = MockResponse()
.setResponseCode(200)
.setBody("[{"id":1,"name":"Test User","email":"test@example.com"}]")
β Best Practices
- Always handle error responses
- Use sealed classes for API state (Success, Error, Loading)
- Avoid exposing Retrofit directly to the UI β use a repository layer
- Use
@JvmSuppressWildcards
when dealing with generics in@Body
π Conclusion
Retrofit is a robust and flexible library for handling network requests in Android. With support for coroutines, Flow, and multiple data converters, it simplifies API communication significantly. Mastering Retrofit can help you build clean, scalable, and maintainable networking layers in your Android apps.