Retrofit
1. Giới thiệu
Phần tiêu đề “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
Phần tiêu đề “2. Setup”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
Phần tiêu đề “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
Phần tiêu đề “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
Phần tiêu đề “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
Phần tiêu đề “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
Phần tiêu đề “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
Phần tiêu đề “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
Phần tiêu đề “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
Phần tiêu đề “10. Compose Integration”@Composablefun 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
Phần tiêu đề “📝 Tóm tắt”| Annotation | Mô tả |
|---|---|
@GET |
HTTP GET request |
@POST |
HTTP POST request |
@PUT |
HTTP PUT request |
@DELETE |
HTTP DELETE request |
@Path |
URL path parameter |
@Query |
URL query parameter |
@Body |
Request body |
@Header |
Request header |
@Multipart |
Multipart request |