Retrofit is a powerful and popular networking library for Android development by Square.
It simplifies the process of making network requests by abstracting away much of the boilerplate code involved in handling APIs.
Retrofit is built on top of OkHttp and offers a type-safe API for making HTTP requests.
Key Features of Retrofit:
- Declarative API: Retrofit allows you to define API endpoints as Java/Kotlin interfaces with annotations specifying the HTTP request type, URL, headers, query parameters, and request body.
- Type Safety: It generates implementation code at compile time based on the interface definitions. This provides compile-time checks for request and response types, reducing runtime errors.
- Serialization: Retrofit integrates seamlessly with popular serialization libraries like Gson or Moshi to automatically convert JSON responses to Java/Kotlin objects and vice versa.
- Support for different HTTP methods: It supports various HTTP methods like GET, POST, PUT, DELETE, etc., and allows customization of request headers, query parameters, and request bodies.
- Interceptor Support: Being built on top of OkHttp, Retrofit leverages OkHttp's powerful features, including interceptors for logging, authentication, or manipulating requests and responses.
Explanation:
- all type of rest api method can be declared in interface and after that it can be fired from retrofit Instance object
- all method get response in json format to store them we will provide model class
and Encapsulate using
Call< >Class of Retrofit.
- Retrofit Builder: You configure Retrofit with a base URL and add converters like GsonConverterFactory to parse JSON responses.
- API Interface: You define the API endpoints as methods in an interface annotated with HTTP methods (e.g.,
@GET,@POST), specifying the endpoint paths and return types.
- Making Requests: Retrofit generates an implementation of the API interface, allowing you to make network requests synchronously or asynchronously. Responses are handled using callback methods.
Retrofit simplifies the process of working with APIs in Android by providing a clean and intuitive way to define and make network requests, handle responses, and parse data. Its type safety and integration with serialization libraries make it a popular choice among Android developers.
Retrofit
Retrofit is a powerful networking library for Android development (by Square).
It simplifies making HTTP requests and parsing API responses by removing boilerplate code.
It works on top of OkHttp and provides a type-safe API for network requests.
Key Features
- Declarative API using annotations
- Type safety with compile-time checks
- JSON serialization support (Gson, Moshi, Kotlin Serialization)
- Supports multiple HTTP methods (GET, POST, PUT, DELETE…)
- Interceptors and OkHttp support
Dependencies Setup
// Retrofit main dependency implementation("com.squareup.retrofit2:retrofit:2.9.0") // JSON Converter Libraries implementation("com.squareup.retrofit2:converter-gson:2.9.0") implementation("com.squareup.retrofit2:converter-moshi:2.9.0") // Coroutines (optional) implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.2") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2")
Basic Usage of Retrofit
1️⃣ Create Retrofit Instance
object RetrofitInstance { private val retrofit by lazy { Retrofit.Builder() .baseUrl("https://api.example.com/") .addConverterFactory(GsonConverterFactory.create()) .build() } val apiService: ApiService by lazy { retrofit.create(ApiService::class.java) } }
2️⃣ Create API Interface
interface ApiService { @GET("endpoint") fun getData(): Call<DataModel> }
3️⃣ Make Request
RetrofitInstance.apiService.getData().enqueue(object : Callback<DataModel> { override fun onResponse(call: Call<DataModel>, response: Response<DataModel>) { if (response.isSuccessful) { val data = response.body() } } override fun onFailure(call: Call<DataModel>, t: Throwable) { t.printStackTrace() } })
Basic Explanation
- Retrofit builder prepares network configuration
• Interface defines API methods using annotations
• API calls return
Call<T> type used with callbacksIntermediate Usage
- (Response Handling, Errors, Query, Path, Body)
GET with Path
@GET("posts/{id}") fun getPostById(@Path("id") id: Int): Call<PostItem>
GET with Query
@GET("posts") fun getPostsByUser(@Query("userId") userId: Int): Call<List<PostItem>
Multiple Query
@GET("posts") fun getPosts( @Query("userId") userId: Int, @Query("_sort") sort: String?, @Query("_order") order: String? ): Call<List<PostItem>>
QueryMap
@GET("posts") fun getPostsQueryMap(@QueryMap params: HashMap<String, String>): Call<List<PostItem>>
Full URL support
@GET fun getCommentsByUrl(@Url url: String): Call<List<Comment>>
POST with Body
@POST("posts") fun createPost(@Body post: PostItem): Call<PostItem>
Advanced Usage
- (Coroutines, Sealed Results, Interceptors)
Using Coroutines instead of Call<T>
interface ApiService { @GET("posts") suspend fun getPosts(): Response<List<PostItem>> }
Usage:
viewModelScope.launch { val response = RetrofitInstance.apiService.getPosts() if (response.isSuccessful) { val list = response.body() } }
Sealed Class Result Wrapper
Better error handling:
sealed class ApiResult<T> { data class Success<T>(val data: T): ApiResult<T>() data class Error<T>(val message: String): ApiResult<T>() }
Repository example:
suspend fun fetchPost(): ApiResult<List<PostItem>> { return try { val res = apiService.getPosts() if (res.isSuccessful) ApiResult.Success(res.body()!!) else ApiResult.Error(res.message()) } catch (e: Exception) { ApiResult.Error(e.localizedMessage ?: "Error") } }
Adding Logging Interceptor
val logging = HttpLoggingInterceptor().apply { level = HttpLoggingInterceptor.Level.BODY } val client = OkHttpClient.Builder() .addInterceptor(logging) .build() val retrofit = Retrofit.Builder() .baseUrl("https://api.example.com/") .client(client) .addConverterFactory(GsonConverterFactory.create()) .build()
Additional Network Request Scenarios
✅ Passing Headers / Token Authentication
// Example: Using OkHttp interceptor globally val client = OkHttpClient.Builder() .addInterceptor { chain -> val original = chain.request() val request = original.newBuilder() .header("Authorization", "Bearer $token") // pass token .header("Accept", "application/json") // other custom header .build() chain.proceed(request) } .build() val retrofit = Retrofit.Builder() .baseUrl("https://api.example.com/") .client(client) .addConverterFactory(GsonConverterFactory.create()) .build()
Or annotate method-specific headers:
interface ApiService { @GET("endpoint") fun getData(@Header("Authorization") token: String): Call<DataModel> }
Explanations:
• Using an interceptor you add headers for all requests. Medium+1
• Using
@Header you set header per method.• Useful when you have a token (Bearer or otherwise) to pass.
✅ Form-URL Encoded Requests (application/x-www-form-urlencoded)
interface ApiService { @FormUrlEncoded @POST("login") fun login( @Field("username") username: String, @Field("password") password: String ): Call<LoginResponse> }
Or using
@FieldMap if keys dynamic:@FormUrlEncoded @POST("submit") fun submitForm(@FieldMap fields: Map<String, String>): Call<SubmitResponse>
Notes:
•
@FormUrlEncoded must be used with @POST (or other method that has a body) — not @GET. futurestud.io+1• Use
@Field for each key/value, or @FieldMap for a map.• This format is typical for older APIs or login endpoints.
✅ Uploading Image/File (Multipart)
interface ApiService { @Multipart @POST("upload") fun uploadFile( @Part file: MultipartBody.Part, @Part("description") description: RequestBody ): Call<UploadResponse> }
Preparing file and parts:
val file = File(path) val requestFile = file.asRequestBody("image/jpeg".toMediaTypeOrNull()) val body = MultipartBody.Part.createFormData("image", file.name, requestFile) val description = "My image upload".toRequestBody("text/plain".toMediaTypeOrNull()) apiService.uploadFile(body, description).enqueue(...)
Notes & caveats:
• Use
@Multipart annotation• Use
MultipartBody.Part for the file part and RequestBody (or @Part("key") RequestBody) for additional fields. Medium• Make sure you’ve got proper permissions if reading from external storage.
✅ Putting It All Together
For example, if you need to upload a profile picture with authorization and other fields:
// Retrofit setup with interceptor as above // ... interface ApiService { @Multipart @POST("user/profile/upload") fun uploadProfile( @Header("Authorization") token: String, @Part image: MultipartBody.Part, @Part("userId") userId: RequestBody ): Call<GenericResponse> }
Usage:
val tokenHeader = "Bearer $token" val file = File(imagePath) val imagePart = MultipartBody.Part.createFormData( "profile_pic", file.name, file.asRequestBody("image/jpeg".toMediaTypeOrNull()) ) val userIdPart = userId.toString() .toRequestBody("text/plain".toMediaTypeOrNull()) apiService.uploadProfile(tokenHeader, imagePart, userIdPart) .enqueue(…)