Kotlin实现Retrofit风格的网络请求封装

发布于:2025-07-30 ⋅ 阅读:(14) ⋅ 点赞:(0)

1. 添加依赖

确保在 build.gradle 文件中添加了必要的依赖:

dependencies {
    // Retrofit
    implementation 'com.squareup.retrofit2:retrofit:2.9.0'
    implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
    implementation 'com.squareup.okhttp3:logging-interceptor:4.9.3'

    // Kotlin Coroutines
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3'
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3'

    // Lifecycle (用于协程的 lifecycleScope)
    implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.6.2'
    implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2'
}

2. 定义数据模型

定义数据模型类,例如 User

data class User(
    val id: Int,
    val name: String,
    val email: String
)

3. 定义 API 接口

使用 Retrofit 的注解定义 API 接口:

interface ApiService {
    @GET("users/{id}")
    suspend fun getUser(@Path("id") userId: Int): Response<User>

    @GET("users")
    suspend fun getUsers(): Response<List<User>>

    @POST("refresh_token")
    suspend fun refreshToken(): Response<TokenResponse>
}

4. 创建 Retrofit 实例

创建 Retrofit 实例并配置它,包括日志拦截器、失败重试拦截器和 Token 刷新拦截器:

object RetrofitClient {
    private const val BASE_URL = "https://api.example.com/"
    private const val MAX_RETRIES = 3

    val apiService: ApiService by lazy {
        Retrofit.Builder()
            .baseUrl(BASE_URL)
            .addConverterFactory(GsonConverterFactory.create())
            .client(okHttpClient())
            .build()
            .create(ApiService::class.java)
    }

    private fun okHttpClient(): OkHttpClient {
        val loggingInterceptor = HttpLoggingInterceptor()
        loggingInterceptor.level = HttpLoggingInterceptor.Level.BODY

        val retryInterceptor = RetryInterceptor(MAX_RETRIES)

        val tokenRefreshInterceptor = TokenRefreshInterceptor()

        return OkHttpClient.Builder()
            .addInterceptor(loggingInterceptor)
            .addInterceptor(retryInterceptor)
            .addInterceptor(tokenRefreshInterceptor)
            .build()
    }
}

5. 实现拦截器

5.1 日志拦截器

日志拦截器已经通过 HttpLoggingInterceptor 实现。

5.2 失败重试拦截器

实现一个失败重试拦截器:

class RetryInterceptor(private val maxRetries: Int) : Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        var retries = 0
        var request = chain.request()
        var response: Response? = null

        while (retries < maxRetries) {
            try {
                response = chain.proceed(request)
                if (response.isSuccessful) {
                    return response
                }
            } catch (e: IOException) {
                // Log the exception
            }
            retries++
        }
        return response ?: throw IOException("Failed to retry request")
    }
}
5.3 Token 刷新拦截器

实现一个 Token 刷新拦截器:

class TokenRefreshInterceptor : Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        val request = chain.request()
        val response = chain.proceed(request)

        if (response.code == 401) { // Token 失效
            val refreshTokenResponse = RetrofitClient.apiService.refreshToken()
            if (refreshTokenResponse.isSuccessful) {
                val newToken = refreshTokenResponse.body()?.token
                if (newToken != null) {
                    // 更新 Token
                    // 重新发送请求
                    val newRequest = request.newBuilder()
                        .header("Authorization", "Bearer $newToken")
                        .build()
                    return chain.proceed(newRequest)
                }
            }
        }
        return response
    }
}

6. 封装网络请求

封装网络请求逻辑,使用协程和 Flow:

import kotlinx.coroutines.flow.flow
import retrofit2.Response

object NetworkRepository {
    suspend fun fetchUser(userId: Int) = RetrofitClient.apiService.getUser(userId)

    suspend fun fetchUsers() = RetrofitClient.apiService.getUsers()
}

7. 在 ViewModel 中使用封装的网络请求

在 ViewModel 中调用封装的网络请求,并使用 StateFlowLiveData 管理状态:

import androidx.lifecycle.ViewModel
import androidx.lifecycle.asLiveData
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.flow.*

class MainViewModel : ViewModel() {
    private val _networkState = MutableStateFlow<NetworkState<List<User>>>(NetworkState.Loading())
    val networkState = _networkState.asLiveData()

    init {
        fetchUsers()
    }

    private fun fetchUsers() {
        viewModelScope.launch {
            _networkState.value = NetworkState.Loading()
            try {
                val response = NetworkRepository.fetchUsers()
                if (response.isSuccessful) {
                    _networkState.value = NetworkState.Success(response.body()!!)
                } else {
                    _networkState.value = NetworkState.Error(Exception("Error: ${response.code()}"))
                }
            } catch (e: Exception) {
                _networkState.value = NetworkState.Error(e)
            }
        }
    }
}

8. 在 Activity 或 Fragment 中观察数据

在 Activity 或 Fragment 中观察 ViewModel 中的数据,并更新 UI:

class MainActivity : AppCompatActivity() {
    private val viewModel: MainViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        viewModel.networkState.observe(this) { state ->
            when (state) {
                is NetworkState.Loading -> {
                    // Show loading
                }
                is NetworkState.Success -> {
                    // Update UI with users
                }
                is NetworkState.Error -> {
                    // Show error
                }
            }
        }
    }
}

9. 错误处理和加载状态管理

可以进一步封装错误处理和加载状态管理,例如使用密封类来表示网络请求的状态:

sealed class NetworkState<out T : Any> {
    class Loading<out T : Any> : NetworkState<T>()
    data class Success<out T : Any>(val data: T) : NetworkState<T>()
    data class Error<out T : Any>(val exception: Exception) : NetworkState<T>()
}

通过以上步骤,你可以实现一个高效、简洁且易于维护的 Retrofit 风格的网络请求封装,结合协程和 Flow,使得网络请求的代码更加优雅。


网站公告

今日签到

点亮在社区的每一天
去签到