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 架构中最经典的数据流向。

相关文章
|
2月前
|
存储 Rust NoSQL
一条命令迁移,帮你实现 OpenClaw 与 Hermes Agent 记忆互通!
本文是基于阿里云 Tablestore 的 Agent 记忆共享实战指南:一条命令迁移 OpenClaw 记忆至 Hermes,通过统一 Tablestore 实例、应用 ID 与租户 ID,实现跨Agent(如龙虾与马)记忆自动互通、实时同步与语义检索,支持 CLI 管理与对话中直接调用,安全可靠,开箱即用。
2970 122
|
1月前
|
人工智能 安全 前端开发
来不及学 AI 就不用学了?——从 AI 到 Agent,再到 Harness Engineering 与 Loop Engineering :一条被产品化加速的技术演进线
本文是一份AI工程演进图谱,以「概念地图」形式梳理2022–2026年LLM→Agent→Harness→Loop的演进脉络,厘清OpenClaw、MCP、Skills等核心概念的关系,破除术语迷雾,强调Harness Engineering才是当前最值得投入的系统性工程能力。(239字)
282 1
|
1月前
|
人工智能 JavaScript 定位技术
CodeGraph+Hermes Agent组合部署构建代码地图教程:代码智能协同实操指南
在2026年编程AI工具高速发展的当下,各类编程Agent已经成为开发者日常工作的重要助手,无论是代码编写、问题排查、项目重构还是接口调试,AI智能体都能大幅降低人工成本。但在处理中大型代码仓库时,传统编程Agent普遍存在明显短板:需要反复调用文件读取、检索、目录查看等工具,不断遍历项目文件梳理代码结构,不仅消耗大量Token资源,还会拉长任务执行时长,工作效率大打降。而**CodeGraph**的出现完美解决了这一行业痛点,它依托AST语法树与本地知识图谱技术,提前为代码库构建结构化“代码地图”,让编程Agent无需重复探索项目结构。结合当下热门的Hermes Agent智能体框架,二者形
456 0
|
1月前
|
数据采集 人工智能 安全
AI 企业知识库系统的开发
本项目开发基于RAG技术的AI企业知识库系统,通过文档解析、智能分块、向量化索引与双路检索重排,让大模型精准调用私有资料生成可靠回答。支持多格式文档处理、细粒度权限管控及在线反馈优化,助力企业安全高效落地AI知识管理。(239字)
|
1月前
|
人工智能 运维 API
知识库分层编排:从 RAG 到 Agent-native Knowledge Context Layer
本文剖析知识库根本困境,系统对比Naive RAG、LLM Wiki、Graphify、GraphRAG四大范式,提出“金字塔”结构化知识工程新范式:按稳定性与抽象度分五层(原则→架构→规范→实现→经验),结合角色感知与知识图谱关联,实现层次定位、跨层导航与增量同步,显著提升检索精度与知识保鲜能力。
507 0
|
1月前
|
SQL 数据采集 人工智能
verify-data:一个端到端的数据验数 Agent Skill
本文介绍了一个面向数据开发团队的端到端数据验数 Agent Skill——verify-data。该技能通过自然语言交互,自动完成从表结构获取、基准表发现、代码逻辑分析、验数 SQL 生成、执行到报告发布的全流程,将传统手工验数从"手写多条 SQL + 人工比对"升级为"一句话触发 + 评审级证据输出"。文章从背景痛点、核心架构与能力、实战场景、设计原则、踩坑经验、当前优势与挑战等方面系统展开,并在最后给出未来演进方向的思考,希望为同样在做数据质量保障和 Agent 工具落地的开发者提供参考。
173 0
|
存储 缓存 NoSQL
跟着源码学IM(十一):一套基于Netty的分布式高可用IM详细设计与实现(有源码)
本文将要分享的是如何从零实现一套基于Netty框架的分布式高可用IM系统,它将支持长连接网关管理、单聊、群聊、聊天记录查询、离线消息存储、消息推送、心跳、分布式唯一ID、红包、消息同步等功能,并且还支持集群部署。
14117 1
|
2月前
|
JSON 前端开发 测试技术
Kimi-k2.6 流式回包乱序后,我这样接入 ​D​М‌X​Α‌РΙ
kimi-k2.6 不止于聊天,其核心价值在于“可执行交付”:统一支持代码生成、长时程任务、Agent协作、文档→技能复用及多格式输出,具备工程级组合能力。它契合企业对“单模型多工位”的刚需——在研发、内容中台等场景中,稳定闭环完成需求拆解、编码、文档整理等多步任务。真正落地需依托DMXAPI网关实现标准化API集成,解决Web路径的不确定性,让模型能力成为可度量、可审计、可持续的生产基础执行层。(239字)
|
7月前
|
监控 安全 物联网
化工厂人员定位技术从系统架构到核心功能详解(一)
化工厂人员定位技术以UWB高精度定位为核心,融合物联网与大数据,构建五层系统架构,实现人员实时定位、电子围栏预警、一键SOS报警及应急联动,提升高危区域安全管控与应急响应能力。如果您想进一步了解定位的案例,欢迎关注、评论留言~也可搜索lbs智能定位。
|
3月前
|
SQL 机器学习/深度学习 自然语言处理
运营日报自动化:智能问数如何实现“开口即得”?
截至2026年4月初,智能问数技术在运营日报自动化场景中已形成多元实现路径。部分方案依赖预置宽表与指标层,通过自然语言匹配固定查询模板,适合结构稳定、问题明确的“开卷考试”式场景;另一些则基于动态Text2SQL或语义本体建模,试图应对更开放的跨域提问,但对数据治理和语义一致性要求较高。不同路线在前期建设成本、后期扩展性及准确率上各有权衡:前者上线快、维护简单,后者泛化能力强但需持续投入知识治理。实践中,企业往往根据自身数据成熟度与业务复杂度选择适配方案,并非单一技术可通解所有“开口即得”需求。