网络请求:Retrofit集成、OkHttp配置、协程与网络请求结合、JSON解析

网络请求,说白了就是App跟服务器打交道的那扇门。我做了这么多年Android,见过太多因为网络层写得不好而翻车的项目。今天咱们就把这扇门彻底整明白。

一、Retrofit集成——最优雅的HTTP客户端

Retrofit这东西,我刚开始用的时候就觉得——嗯,这才是人该写的代码。它把网络请求抽象成了接口调用,你只需要定义接口,剩下的它帮你搞定。

1.1 添加依赖

build.gradle.kts里加上:

// Retrofit
implementation("com.squareup.retrofit2:retrofit:2.9.0")
// 配合OkHttp
implementation("com.squareup.okhttp3:okhttp:4.12.0")
// JSON转换器(这里用Moshi举例)
implementation("com.squareup.retrofit2:converter-moshi:2.9.0")

我个人习惯把版本号抽到libs.versions.toml里统一管理,项目大了你就知道这有多香了。

1.2 定义API接口

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

    @POST("users")
    suspend fun createUser(@Body user: User): User
}

注意看,这里用了suspend关键字。为什么?因为我们要跟协程配合,这个后面细说。

二、OkHttp配置——网络层的守门员

Retrofit底层跑的是OkHttp。你想想看,如果OkHttp没配好,Retrofit再优雅也没用。

2.1 基础配置

val okHttpClient = OkHttpClient.Builder()
    .connectTimeout(30, TimeUnit.SECONDS)
    .readTimeout(30, TimeUnit.SECONDS)
    .writeTimeout(30, TimeUnit.SECONDS)
    .addInterceptor(HttpLoggingInterceptor().apply {
        level = HttpLoggingInterceptor.Level.BODY
    })
    .build()

我在项目中遇到过一个问题:线上环境忘了关日志拦截器,结果用户敏感信息全打出来了……嗯,从那以后我养成了用BuildConfig.DEBUG控制日志级别的习惯。

避坑指南:我曾经在生产环境忘记移除日志拦截器,导致用户手机日志文件里塞满了接口数据。建议用if (BuildConfig.DEBUG) addInterceptor(...)来条件编译。

2.2 拦截器实战

拦截器是OkHttp的灵魂。我常用的几个:

  • 日志拦截器:调试必备,但记得只在debug模式开启
  • Token刷新拦截器:自动处理401,用户无感知
  • 缓存拦截器:离线也能看数据
class TokenInterceptor : Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        val originalRequest = chain.request()
        val newRequest = originalRequest.newBuilder()
            .header("Authorization", "Bearer ${getToken()}")
            .build()
        return chain.proceed(newRequest)
    }
}

三、协程与网络请求结合——告别回调地狱

以前写网络请求,回调嵌套回调,代码丑得自己都不想看。协程来了之后,整个世界清静了。

3.1 为什么用协程?

说白了,协程让异步代码写起来像同步代码。你不需要手动切线程,不需要回调,一个suspend搞定。

// 以前:回调地狱
api.getUser(1, object : Callback<User> {
    override fun onResponse(call: Call<User>, response: Response<User>) {
        // 处理成功
    }
    override fun onFailure(call: Call<User>, t: Throwable) {
        // 处理失败
    }
})

// 现在:协程
viewModelScope.launch {
    try {
        val user = api.getUser(1)
        // 直接使用user
    } catch (e: Exception) {
        // 统一处理异常
    }
}

你想想看,哪个更清爽?

3.2 协程的异常处理

我建议统一封装一个网络请求的扩展函数:

suspend fun <T> safeApiCall(call: suspend () -> T): Result<T> {
    return try {
        Result.success(call())
    } catch (e: Exception) {
        when (e) {
            is HttpException -> {
                // 根据状态码处理
                Result.failure(e)
            }
            is IOException -> {
                // 网络问题
                Result.failure(e)
            }
            else -> {
                // 其他异常
                Result.failure(e)
            }
        }
    }
}
小技巧:Result类型包装返回值,调用方可以很优雅地处理成功/失败两种情况,不用到处写try-catch。

四、JSON解析——数据格式化的最后一公里

网络请求拿回来的数据是JSON字符串,得转成Kotlin对象才能用。这里有两个主流选择:kotlinx.serializationMoshi

4.1 kotlinx.serialization——Kotlin亲儿子

这是JetBrains官方出的,跟Kotlin语言深度绑定。用起来很简单:

@Serializable
data class User(
    val id: Int,
    val name: String,
    @SerialName("avatar_url")
    val avatarUrl: String
)

配合Retrofit使用时,需要加一个转换器:

implementation("com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:1.0.0")

4.2 Moshi——Square家的老牌选手

Moshi是Square出的,跟Retrofit同门,配合起来天衣无缝。我个人更习惯用Moshi,因为它的自定义适配器写起来很顺手。

@JsonClass(generateAdapter = true)
data class User(
    @Json(name = "id")
    val id: Int,
    @Json(name = "name")
    val name: String,
    @Json(name = "avatar_url")
    val avatarUrl: String
)

4.3 怎么选?

对比项 kotlinx.serialization Moshi
编译速度 快(编译期处理) 中等(运行时反射)
Kotlin特性支持 完美(默认参数、data class) 良好(需额外配置)
自定义解析 需要写Serializer 写JsonAdapter,更灵活
社区生态 较新,但官方支持 成熟,文档丰富

我的建议是:新项目用kotlinx.serialization,老项目迁移成本高就继续用Moshi。两个都能用,别纠结。

五、完整集成示例

把上面所有东西串起来,就是一个完整的网络层:

// 1. 创建OkHttpClient
val okHttpClient = OkHttpClient.Builder()
    .connectTimeout(30, TimeUnit.SECONDS)
    .addInterceptor(TokenInterceptor())
    .addInterceptor(if (BuildConfig.DEBUG) HttpLoggingInterceptor().apply {
        level = HttpLoggingInterceptor.Level.BODY
    } else Interceptor { chain -> chain.proceed(chain.request()) })
    .build()

// 2. 创建Retrofit实例
val retrofit = Retrofit.Builder()
    .baseUrl("https://api.example.com/")
    .client(okHttpClient)
    .addConverterFactory(Json.asConverterFactory("application/json".toMediaType()))
    .build()

// 3. 创建API服务
val api = retrofit.create(ApiService::class.java)

// 4. 在ViewModel中使用
class UserViewModel : ViewModel() {
    private val _user = MutableStateFlow<User?>(null)
    val user: StateFlow<User?> = _user.asStateFlow()

    fun loadUser(id: Int) {
        viewModelScope.launch {
            val result = safeApiCall { api.getUser(id) }
            result.onSuccess { _user.value = it }
                 .onFailure { /* 处理错误 */ }
        }
    }
}
核心要点:网络层要关注三件事——连接配置(OkHttp)、接口定义(Retrofit)、数据转换(JSON解析)。协程把这三件事串起来,让代码变得简洁又可靠。

六、知识体系总览

下面这张图把本章的核心脉络画出来了,你可以对照着回顾:

网络请求知识体系 Retrofit 接口定义 + 注解 OkHttp 连接池 + 拦截器 协程 suspend + 异常处理 JSON解析 kotlinx / Moshi Retrofit 核心 @GET / @POST / @Path / @Body ConverterFactory 转换器 CallAdapter 适配器 OkHttp 核心 连接超时 / 读写超时 日志拦截器 / Token拦截器 缓存策略 / 重试机制 协程 核心 viewModelScope.launch safeApiCall 封装 Result类型处理 JSON 核心 @Serializable / @JsonClass @SerialName / @Json 自定义适配器 最终产出:稳定、可维护的网络层 Retrofit + OkHttp + 协程 + JSON解析 = 优雅的网络请求

嗯,到这里网络请求这块就讲完了。记住一个原则:网络层要稳定,接口要清晰,异常要兜底。你在项目里按这个思路走,基本不会出大问题。


公众号:蓝海资料掘金营,微信deep3321