OkHttp 到底是个啥?我为什么非要用它?
说起来挺有意思。2013 年我刚接触 Android 开发那会儿,网络请求基本靠 HttpURLConnection 硬写,或者直接用 HttpClient。那时候代码写起来可痛苦了——动不动就线程卡死,回调嵌套得跟俄罗斯套娃似的。后来 Square 公司搞出了 OkHttp,我第一次用就感觉:「嗯,这才对嘛。」
OkHttp 说白了就是一个高效的 HTTP 客户端库。但它不止是发个请求那么简单。它在底层帮你处理了连接复用、超时重试、请求压缩这些脏活累活。你只管调 API,剩下的事它包了。
我个人习惯把 OkHttp 看作 Android 网络层的「基础设施」。它不只是个请求工具,更是协程、拦截器、连接池这些高级玩意的地基。今天这节课,咱先聊聊它的来头、核心能力,再上手跑第一个请求。
OkHttp 的简短历史
Square 公司在 2013 年开源了 OkHttp 1.0。我记得当时 Android 官方还在推 HttpClient,但 OkHttp 凭借 SPDY 协议(HTTP/2 的前身)和内置的 Gzip 压缩,一下子就火了。2019 年,OkHttp 成为 Android 系统内置的 HTTP 客户端(Android 4.4+ 起),Java 生态里也慢慢成了事实标准。
2023 年 OkHttp 5.0 已经支持 Kotlin 协程、HTTP/2 和 HTTP/3(QUIC),性能又上了一个台阶。别小看这几个版本号——越往后,我对它的信心就越足。
核心特性一览
我把它最重要的特点列了个表。你可以在项目里对照着用:
| 特性 | 作用说明 | 我的个人体会 |
|---|---|---|
| 连接池 | 复用 TCP 连接,减少握手开销 | 我在做直播推流时,频繁请求全靠它省了 30% 的延迟 |
| 拦截器链 | 可在请求前后插入日志、重试、缓存等逻辑 | 有一次线上问题就是因为拦截器里忘了添加默认 header |
| HTTP/2 多路复用 | 一个连接里并行发多个请求 | 省端口,省资源,尤其适合图片批量加载 |
| 自动重试 & 重定向 | 遇到连接失败自动重试,跟随 3xx 跳转 | 省了你写一大坨 try-catch 的精力 |
| Gzip/Deflate 压缩 | 默认开启,减少传输体积 | 大部分场景下省流量,我建议别关 |
| 协程 & Flow 支持 | 与 Kotlin 协程无缝集成 | 这就是我们后续章节的重点,别急 |
知识体系图:OkHttp 核心架构
先画张图让你有个全局印象。别看它复杂,核心其实就是「请求 -> 拦截器链 -> 网络层 -> 响应」这条线。
application interceptor 里记时,效果很直接。
第一个请求:发个 GET 看看
别光说不练。我们来写一个最简例子:请求知乎的接口(假设它能访问),然后拿回 JSON 打印出来。注意,这里我故意忽略了异步线程——你可以在 Activity 或 ViewModel 里直接跑。
// 1. 创建 OkHttpClient 实例(通常全局单例)
val client = OkHttpClient.Builder()
.connectTimeout(10, java.util.concurrent.TimeUnit.SECONDS)
.readTimeout(10, java.util.concurrent.TimeUnit.SECONDS)
.build()
// 2. 构造请求
val request = Request.Builder()
.url("https://api.zhihu.com/topics/19551402?limit=10")
.header("User-Agent", "OkHttpDemo")
.build()
// 3. 同步执行(别在主线程跑!)
val response = client.newCall(request).execute()
if (response.isSuccessful) {
val body = response.body?.string()
println("Response: $body")
} else {
println("Error: ${response.code}")
}
你可能会问:「怎么不用协程?」别急——这一章咱们先搞明白同步调用。协程会在后面展开。但有一点我提醒你:同步调用必须开启新线程,否则主线程会 ANR 崩溃。我以前在 demo 里忘开线程,直接闪退,当场脸都绿了。
execute(),结果 NetworkOnMainThreadException 扑面而来。OKHttp 默认不让你在主线程发请求。请使用异步 enqueue() 或协程。
这代码结构到底啥意思?
我拆解一下几个核心对象:
- OkHttpClient — 全局配置。建议用单例,避免重复创建连接池。每个 app 一个就好。
- Request — 每次请求的配置。包括 URL、header、method(默认 GET)。
- Call — 一次实际执行的任务。可以
execute()同步或enqueue()异步。 - Response — 返回结果。里面有 body(流或字符串)、code、headers。
说白了,就跟发快递一样:你填好地址(Request),交给快递公司(OkHttpClient),快递员去送(Call),最后你签收(Response)。
异步调用的简单写法
上面是同步,咱再看看异步。Android 里最常见的用法:
client.newCall(request).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
// 网络失败或超时,回调在子线程
Log.e("OkHttp", "请求失败", e)
}
override fun onResponse(call: Call, response: Response) {
// 响应返回,仍在子线程
if (response.isSuccessful) {
val data = response.body?.string()
// 别忘了切回主线程更新UI
}
}
})
这里有个坑:onResponse 和 onFailure 都在 OkHttp 的工作线程里跑的。你如果要更新 UI,必须 runOnUiThread 或者用 Handler。我用 ViewModel + LiveData 时,直接在 ViewModel 里切换协程上下文,舒服多了。
本章要点速览
| 知识点 | 一句话搞清楚 |
|---|---|
| OkHttp 是什么 | 一个高效、可扩展的 HTTP 客户端库,Android 和 Java 生态的标配 |
| 核心特性 | 连接池、拦截器、HTTP/2 多路复用、自动重试、压缩、协程支持 |
| 第一个请求 | 创建 OkHttpClient → 构造 Request → 调用 Call 同步/异步 → 处理 Response |
好,第一章就到这儿。你跑通上面那个示例了吗?如果报了 IOException: failed to connect,多半是网络问题,或者 URL 换一个能用的。别灰心,调试本身就是学习的一部分。