OkHttp 到底是个啥?我为什么非要用它?

说起来挺有意思。2013 年我刚接触 Android 开发那会儿,网络请求基本靠 HttpURLConnection 硬写,或者直接用 HttpClient。那时候代码写起来可痛苦了——动不动就线程卡死,回调嵌套得跟俄罗斯套娃似的。后来 Square 公司搞出了 OkHttp,我第一次用就感觉:「嗯,这才对嘛。」

OkHttp 说白了就是一个高效的 HTTP 客户端库。但它不止是发个请求那么简单。它在底层帮你处理了连接复用、超时重试、请求压缩这些脏活累活。你只管调 API,剩下的事它包了。

我个人习惯把 OkHttp 看作 Android 网络层的「基础设施」。它不只是个请求工具,更是协程、拦截器、连接池这些高级玩意的地基。今天这节课,咱先聊聊它的来头、核心能力,再上手跑第一个请求。

一句话总结: 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 核心架构

先画张图让你有个全局印象。别看它复杂,核心其实就是「请求 -> 拦截器链 -> 网络层 -> 响应」这条线。

OkHttp 核心架构图 应用代码(你的业务层) 拦截器链(Interceptors) 连接池(ConnectionPool) DNS 解析 & 路由 Socket 连接 & HTTP/2 多路复用 响应反序列化(JSON / 流) 注意:连接池和DNS 解析由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 里忘开线程,直接闪退,当场脸都绿了。

避坑指南: 我曾经在 Android 主线程上调 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
我个人的建议: 刚上手 OkHttp 时,先搞懂同步和异步,再琢磨拦截器。别一口吃成胖子。下一章咱们会深入拦截器的世界——那才是真正的「魔法」所在。

好,第一章就到这儿。你跑通上面那个示例了吗?如果报了 IOException: failed to connect,多半是网络问题,或者 URL 换一个能用的。别灰心,调试本身就是学习的一部分。