Retrofit + OkHttp 网络请求

简介: Android网络开发事实标准:OkHttp负责高效底层通信(连接池、拦截器、超时等),Retrofit提供声明式接口封装,支持注解定义API、协程挂起函数与自动JSON序列化。二者结合大幅简化HTTP请求,提升可读性与健壮性。

背景

几乎没有 Android 应用不需要联网。列表加载、用户登录、上传图片、推送消息——这些功能的背后都有一个共同的基石:HTTP 网络请求。早期的 Android 开发常用 HttpURLConnection 或 Apache HttpClient,写起来样板代码多、线程管理繁琐、解析响应还得手写 JSON 解析逻辑。

直到 Square 公司先后开源了 OkHttp 和 Retrofit,局面才发生根本性改变。OkHttp 是一个高效、可定制的 HTTP 客户端,处理连接池、缓存、重定向、拦截器等底层细节。Retrofit 则在 OkHttp 之上做了一层声明式抽象,把 HTTP API 变成 Java/Kotlin 接口,开发者只需定义接口方法加几个注解,Retrofit 自动完成请求构造、参数序列化、响应反序列化。

一句话总结:OkHttp 管网络传输,Retrofit 管接口封装,两者配合是 Android 网络层的事实标准。

核心概念

Retrofit 接口声明

Retrofit 的核心思想是"接口即 API"。你不需要手动拼接 URL、设置请求头、构造 RequestBody,只需要:

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

@GET 指定请求方式,@Path 替代路径参数,suspend 标记为挂起函数后可直接在协程中调用。Retrofit 在运行时会动态生成接口的实现类,你拿到的就是一个"活的"接口实例。

OkHttp 的作用

每次 Retrofit 请求最终都会交给 OkHttp 去真正发出去。OkHttp 负责连接建立、TLS 握手、请求排队、连接复用、超时控制。通过拦截器机制,你可以在请求发出前和响应返回后做很多事情:添加公共请求头、打印日志、重试失败请求、缓存策略等。

数据转换器

Retrofit 本身不关心响应体是什么格式,需要配合 Converter 来转换。最常见的是 Moshi 或 Gson:

Retrofit.Builder()
    .addConverterFactory(MoshiConverterFactory.create())

这样 Retrofit 就会自动把 JSON 响应反序列化成你指定的 Kotlin 数据类。

代码实战(Kotlin)

1. 添加依赖

// build.gradle.kts (module)
dependencies {
    implementation("com.squareup.retrofit2:retrofit:2.11.0")
    implementation("com.squareup.retrofit2:converter-moshi:2.11.0")
    implementation("com.squareup.okhttp3:okhttp:4.12.0")
    implementation("com.squareup.okhttp3:logging-interceptor:4.12.0")
    implementation("com.squareup.moshi:moshi-kotlin:1.15.1")
}

2. 定义数据类和接口

import com.squareup.moshi.JsonClass
import retrofit2.Response
import retrofit2.http.GET
import retrofit2.http.Query

@JsonClass(generateAdapter = true)
data class User(
    val id: Long,
    val name: String,
    val email: String
)

@JsonClass(generateAdapter = true)
data class UserListResponse(
    val data: List<User>,
    val total: Int
)

interface ApiService {
    @GET("api/users")
    suspend fun getUsers(
        @Query("page") page: Int = 1,
        @Query("limit") limit: Int = 20
    ): Response<UserListResponse>
}

3. 配置 Retrofit 客户端

import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import retrofit2.converter.moshi.MoshiConverterFactory
import java.util.concurrent.TimeUnit

object RetrofitClient {

    private const val BASE_URL = "https://api.example.com/"

    private val okHttpClient: OkHttpClient by lazy {
        val logging = HttpLoggingInterceptor().apply {
            level = HttpLoggingInterceptor.Level.BODY
        }
        OkHttpClient.Builder()
            .connectTimeout(30, TimeUnit.SECONDS)
            .readTimeout(30, TimeUnit.SECONDS)
            .addInterceptor { chain ->
                val request = chain.request().newBuilder()
                    .addHeader("Authorization", "Bearer YOUR_TOKEN")
                    .addHeader("Accept", "application/json")
                    .build()
                chain.proceed(request)
            }
            .addInterceptor(logging)
            .build()
    }

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

4. ViewModel 中调用

import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.launch

class UserListViewModel : ViewModel() {

    private val _users = MutableLiveData<List<User>>()
    val users: LiveData<List<User>> = _users

    private val _error = MutableLiveData<String>()
    val error: LiveData<String> = _error

    fun loadUsers(page: Int = 1) {
        viewModelScope.launch {
            try {
                val response = RetrofitClient.apiService.getUsers(page)
                if (response.isSuccessful) {
                    _users.value = response.body()?.data ?: emptyList()
                } else {
                    _error.value = "请求失败:${response.code()}"
                }
            } catch (e: Exception) {
                _error.value = "网络异常:${e.message}"
            }
        }
    }
}

核心要点:用 viewModelScope.launch 发起协程,Retrofit 的 suspend 函数自动在后台线程执行网络请求,不需要手动切线程。Response<T> 可以同时拿到状态码和解析后的 body,方便做错误处理。

避坑指南

baseUrl 要以斜杠结尾。 如果写 "https://api.example.com" 而不是 "https://api.example.com/",接口中的 /api/users 会被解析为完整的 api/users,导致路径拼接异常。这是新人最容易踩的坑。

不要每次请求都创建 Retrofit 实例。 Retrofit 和 OkHttpClient 中有连接池、线程池等重量资源,创建开销大。应该用单例或依赖注入框架(如 Hilt)管理。

区分 suspend 和 Call。 用 suspend 函数时异常会被抛出,需要在 try-catch 中捕获;用 Call<T> 时则通过 enqueue 回调处理。协程项目建议统一用 suspend。

拦截器顺序很重要。 先添加的拦截器在请求链的外层先执行、内层后执行。日志拦截器一般放在最后,这样能记录最终发出的完整请求,包括前面拦截器添加的请求头。

不要忘记关闭响应体。 虽然 Retrofit 处理了大部分情况,但在用 ResponseBody 时,记得 close。另外,Response<T> 的 body 在协程场景下会自动管理,普通场景下需要留意。

生产环境关闭详细日志。 HttpLoggingInterceptor.Level.BODY 会打印请求体和响应体,可能泄露敏感信息(密钥、用户数据)。上线前建议切换到 Level.NONE 或仅保留 Level.HEADERS

总结

Retrofit + OkHttp 把网络请求从"手写样板代码"变成了"声明接口 + 配置客户端"的简洁范式。你定义数据类描述响应结构,用注解描述 API 形态,OkHttp 保证传输质量,Retrofit 负责胶水代码。配合 Kotlin 协程的 suspend 函数,整个调用链简洁、可读、不易出错。

掌握了这套工具,再结合之前学过的 ViewModel + LiveData,就能搭建出一条完整的数据通道:网络请求(Retrofit)→ 状态管理(ViewModel)→ UI 更新(LiveData)。这正是 MVVM 架构中最经典的数据流向。

相关文章
|
4天前
|
云安全 人工智能 运维
阿里云SecOps Agent,全新安全跨产品执行体验
自然语言驱动 云安全中心/WAF/CFW/ 等多款安全产品联动
1595 2
|
1天前
|
人工智能 定位技术 SEO
我学 GEO 第 15 天:终于知道AI GEO该如何做?
我是暴走的莉莉酱,边旅行边研究AI GEO的数字游民。专注普通人如何提升“AI可见度”——让AI在回答用户问题时准确识别、理解并推荐你。不讲玄学,只做可测、可调、可持续的GEO实践。
348 122
|
4天前
|
机器学习/深度学习 人工智能 调度
🐴 HappyHorse 1.1 现已上线阿里云百炼!快来查收模型使用指南,现在调用享 6 折~
HappyHorse 1.1 是新一代视频生成大模型,全面升级动态表现力、角色一致性、指令遵循、视觉质感与音画协同能力。支持I2V/T2V/R2V三类生成,适配短剧、电商广告、品牌营销等场景,提供高质、流畅、可控的AI视频生产力。
577 3
🐴 HappyHorse 1.1 现已上线阿里云百炼!快来查收模型使用指南,现在调用享 6 折~
|
14天前
|
缓存 测试技术 API
Qwen 3.7 Plus 与 Max 实测:性价比与多模态能力差异解析(2026)
2026 年 6 月 1 日,阿里悄无声息地发布了 Qwen 3.7 Plus,距 Qwen 3.7 Max 上线刚好 11 天。同样的 1M 上下文,同样的 35 小时自治上限。但价格才是头条:Plus 是 0.40/M输入,Max是 2.50/M——便宜约 6 倍——并且还能看图、看视频。Vision Arena 上 Plus 已经排到 #16。所以这周真正值得讨论的问题不是”要不要为视觉能力买单”,而是”Max 凭什么用 6 倍价格换来 2 个百分点的 benchmark 领先”。
|
15天前
|
JavaScript 定位技术 API
CodeGraph 爆火:编程 Agent 需要的不是更多上下文,而是一张提前画好的代码地图
CodeGraph 是一款爆火的本地代码智能工具,通过 tree-sitter 解析 AST 构建结构化知识图谱(存于 SQLite),为编程 Agent 提前生成“代码地图”。它显著降低 Agent 在中大型项目中的探索成本——实测工具调用减少71%、Token 降57%、速度提升46%,支持19+语言及主流框架路由识别,完全离线、无需 API Key。
910 11
CodeGraph 爆火:编程 Agent 需要的不是更多上下文,而是一张提前画好的代码地图
|
8天前
|
缓存 人工智能 运维
GLM 5.2自托管全流程实战:硬件选型、vLLM/SGLang部署与成本盈亏测算
2026年智谱发布GLM 5.2超大混合专家模型,区别于以往仅开放API的闭源大模型,该模型权重以MIT开源协议对外发布,企业与开发者可完整下载、本地审计、私有化部署,实现数据不出环境、自定义微调、自主调度推理资源。GLM 5.2拥有753B总参数,原生支持百万级上下文窗口,在代码生成、长文档推理、数学逻辑等多项基准测试中对标国际顶尖商用模型,是首款可完整自托管的前沿代码向大模型。
651 0
|
2天前
|
消息中间件 人工智能 Kafka
AI 时代,实时入湖正在告别 ETL:从 Kafka 到 Iceberg 的架构减法
本文围绕“零 ETL”这一趋势,讨论流数据入湖为什么需要做架构减法,并结合 Kafka × Table Bucket 的实践,分析一种将通用入湖能力前移到消息与表存储链路中的方案,如何在降低复杂度的同时,兼顾实时性、一致性、Schema 演进、CDC 语义与开放生态兼容。
192 121
|
2天前
|
人工智能 监控 前端开发
Electron 监控:让桌面 Agent 监控触手可及
一行代码实现Electron桌面端全景监控,自动还原崩溃现场、预警内存泄漏、全链路追踪、 SSE流式响应与交互埋点,让 AI 助手运行状态清晰可见,助力快速恢复稳定与流畅。
182 125
|
11天前
|
人工智能 自然语言处理 算法
阿里云百炼Qwen 3.7 Plus与Max实测全解:性价比与多模态能力、成本深度对比
2026年,阿里云百炼平台推出的Qwen 3.7系列成为企业与开发者落地AI应用的核心选择,其中Qwen 3.7 Max与Plus作为两大旗舰版本,定位差异显著:Max是纯文本推理旗舰,专注高强度智能体与复杂逻辑任务;Plus则是多模态全能版,在保留强大文本能力的同时,补齐图像、视频理解能力,且价格大幅降低。本文基于2026年最新实测数据,从核心参数、文本能力、多模态能力、智能体表现、性价比与场景选型六大维度,全面解析两款模型的差异,为用户提供精准选型参考。
537 0