Skip to Content

Retrofit trong Android

1. Giới thiệu

Retrofit là type-safe HTTP client cho Android và Java, giúp dễ dàng consume REST APIs.

2. Setup

// build.gradle.kts dependencies { implementation("com.squareup.retrofit2:retrofit:2.9.0") implementation("com.squareup.retrofit2:converter-gson:2.9.0") implementation("com.squareup.okhttp3:logging-interceptor:4.12.0") }

3. Data Models

data class User( val id: Int, val name: String, val email: String, @SerializedName("avatar_url") val avatarUrl: String ) data class CreateUserRequest( val name: String, val email: String ) data class ApiResponse<T>( val data: T, val message: String, val success: Boolean )

4. API Interface

interface ApiService { @GET("users") suspend fun getUsers(): List<User> @GET("users/{id}") suspend fun getUser(@Path("id") userId: Int): User @GET("users") suspend fun searchUsers( @Query("q") query: String, @Query("page") page: Int = 1, @Query("limit") limit: Int = 20 ): List<User> @POST("users") suspend fun createUser(@Body request: CreateUserRequest): User @PUT("users/{id}") suspend fun updateUser( @Path("id") userId: Int, @Body user: User ): User @DELETE("users/{id}") suspend fun deleteUser(@Path("id") userId: Int) @Multipart @POST("users/{id}/avatar") suspend fun uploadAvatar( @Path("id") userId: Int, @Part image: MultipartBody.Part ): User @Headers("Cache-Control: max-age=300") @GET("config") suspend fun getConfig(): Config }

5. Retrofit Instance

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) .addInterceptor { chain -> val request = chain.request().newBuilder() .addHeader("Authorization", "Bearer ${getToken()}") .addHeader("Accept", "application/json") .build() chain.proceed(request) } .connectTimeout(30, TimeUnit.SECONDS) .readTimeout(30, TimeUnit.SECONDS) .build() private val retrofit = Retrofit.Builder() .baseUrl(BASE_URL) .client(okHttpClient) .addConverterFactory(GsonConverterFactory.create()) .build() val apiService: ApiService = retrofit.create(ApiService::class.java) }

6. Repository Pattern

class UserRepository(private val api: ApiService) { suspend fun getUsers(): Result<List<User>> { return try { val users = api.getUsers() Result.success(users) } catch (e: Exception) { Result.failure(e) } } suspend fun getUser(id: Int): Result<User> { return try { val user = api.getUser(id) Result.success(user) } catch (e: Exception) { Result.failure(e) } } suspend fun createUser(name: String, email: String): Result<User> { return try { val user = api.createUser(CreateUserRequest(name, email)) Result.success(user) } catch (e: Exception) { Result.failure(e) } } }

7. ViewModel Integration

class UserViewModel(private val repository: UserRepository) : ViewModel() { private val _users = MutableStateFlow<UiState<List<User>>>(UiState.Loading) val users: StateFlow<UiState<List<User>>> = _users.asStateFlow() init { loadUsers() } fun loadUsers() { viewModelScope.launch { _users.value = UiState.Loading repository.getUsers() .onSuccess { _users.value = UiState.Success(it) } .onFailure { _users.value = UiState.Error(it.message ?: "Error") } } } } sealed class UiState<out T> { object Loading : UiState<Nothing>() data class Success<T>(val data: T) : UiState<T>() data class Error(val message: String) : UiState<Nothing>() }

8. Error Handling

suspend fun <T> safeApiCall(apiCall: suspend () -> T): Result<T> { return try { Result.success(apiCall()) } catch (e: HttpException) { val errorBody = e.response()?.errorBody()?.string() Result.failure(ApiException(e.code(), errorBody ?: "HTTP Error")) } catch (e: IOException) { Result.failure(NetworkException("Network error")) } catch (e: Exception) { Result.failure(e) } } class ApiException(val code: Int, message: String) : Exception(message) class NetworkException(message: String) : Exception(message)

9. Upload Files

suspend fun uploadImage(file: File): Result<String> { val requestFile = file.asRequestBody("image/*".toMediaTypeOrNull()) val body = MultipartBody.Part.createFormData("image", file.name, requestFile) return safeApiCall { api.uploadAvatar(userId, body) } }

10. Compose Integration

@Composable fun UserListScreen(viewModel: UserViewModel = viewModel()) { val uiState by viewModel.users.collectAsState() when (val state = uiState) { is UiState.Loading -> { CircularProgressIndicator() } is UiState.Success -> { LazyColumn { items(state.data) { user -> UserItem(user) } } } is UiState.Error -> { Column { Text("Error: ${state.message}") Button(onClick = { viewModel.loadUsers() }) { Text("Retry") } } } } }

📝 Tóm tắt

AnnotationMô tả
@GETHTTP GET request
@POSTHTTP POST request
@PUTHTTP PUT request
@DELETEHTTP DELETE request
@PathURL path parameter
@QueryURL query parameter
@BodyRequest body
@HeaderRequest header
@MultipartMultipart request
Last updated on