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

1. 添加依赖

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

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:

kotlin 复制代码
data class User(
    val id: Int,
    val name: String,
    val email: String
)

3. 定义 API 接口

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

kotlin 复制代码
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 刷新拦截器:

kotlin 复制代码
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 失败重试拦截器

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

kotlin 复制代码
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 刷新拦截器:

kotlin 复制代码
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:

kotlin 复制代码
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 中调用封装的网络请求,并使用 StateFlow 或 LiveData 管理状态:

kotlin 复制代码
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:

kotlin 复制代码
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. 错误处理和加载状态管理

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

kotlin 复制代码
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,使得网络请求的代码更加优雅。

相关推荐
黑十28 分钟前
PyTorch 报 “no CUDA GPUs are available“ 怎么办?3 步排查
服务器·网络·人工智能·pytorch·python
updayday8543 小时前
客户附件不该发往旧邮箱,如何在发送前拦截 - Ping64 邮件安全管控
大数据·网络·数据库·安全·智能路由器
其实防守也摸鱼3 小时前
Chrome 扩展安全攻防实录:从攻击面到防御实践
开发语言·前端·网络·chrome·安全·web安全·php
小茬粥3 小时前
QEMU 宿主机网卡实例发现与采集脚本(标准 Linux / Proxmox VE 适配版)COLT_CMDB_QEMU_IFACE.sh
网络
见合八方4 小时前
借助半导体光放大器实现功率预算超31dB的112.5Gbit/s长距无源光网络
网络·5g·soa·光通信·激光雷达·半导体光放大器
夜雪一千4 小时前
锐捷天蝎路由器WiFi优化设置教程:解决邻居WiFi干扰、远距离卡顿
网络·智能路由器
2301_780789664 小时前
深度伪造即服务:新型网络诈骗的“完美面具”与识别之道
java·网络·ddos
lazy H5 小时前
【计算机网络】第 15 篇:Java BIO、NIO 与高并发网络通信——从 Socket 理解 I/O 模型
网络·后端·tcp/ip·计算机网络·nio·408
垆边人似月.5 小时前
RDMA 文件传输踩坑(四):从固定延时改造为 READY/ACK 协议
linux·服务器·网络
乐维_lwops6 小时前
汽车集团网管平台建设案例:乐维网管工具实现跨区域网络统一监控与链路可视化
网络·网管平台·it监控·链路·统一监控