OkHttp 框架简介与核心概念
老实说,我第一次接触 OkHttp 是在 2015 年做的一个即时通讯项目。那时候 Android 网络库还群雄割据,HttpURLConnection 难用,Volley 又不够灵活。直到我试了 OkHttp——嗯,感觉就像从手动挡换成了自动挡,而且还配了涡轮增压。
OkHttp 是 Square 公司开源的 HTTP 客户端,2013 年发布第一个版本。不到十年,它就成了 Android 开发者的标配。Google 甚至在 Android 4.4 之后把内部的 HttpURLConnection 实现换成了 OkHttp。这件事让我印象很深——连官方都认可的东西,你想想看,质量差不了。
从历史看设计哲学
我记得 OkHttp 1.0 的时候还只是个简单的同步 HTTP 客户端。到了 2.0 引入了拦截器(Interceptor),这个设计太聪明了——它把请求/响应的处理链条化,你可以插入日志、重试、缓存、甚至 mock 数据。我后来在项目中做网络监控时,就靠拦截器悄无声息地采集了所有请求耗时。
当前稳定版是 OkHttp 4.x,它用 Kotlin 重写了内部实现,但对外 API 完全兼容 Java。说白了,你以前写的 OkHttp 3.x 代码,一个字都不用改就能跑到 4.x 上。这种向下兼容的态度,值得尊重。
三大核心类:OkHttpClient、Request、Call
一开始接触 OkHttp,你只需要搞懂三个类。我把它们比作一场外卖订单:OkHttpClient 是厨房(配置),Request 是菜单(描述),Call 是骑手(执行)。
| 核心类 | 职责 | 典型配置 |
|---|---|---|
OkHttpClient |
管理连接池、超时、拦截器、缓存等全局配置 | 连接超时 10s, 拦截器列表, CookieJar |
Request |
描述一次 HTTP 请求:URL、方法、Headers、Body | GET/POST, 添加 Header, 设置请求体 |
Call |
代表一次即将或正在执行的请求,可同步/异步发起 | enqueue() 异步, execute() 同步 |
Builder 模式:让配置不再头疼
我猜你一定见过这种代码:
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.addInterceptor(new LoggingInterceptor())
.cookieJar(new PersistentCookieJar())
.build();
Request request = new Request.Builder()
.url("https://api.example.com/data")
.header("Authorization", "Bearer xxx")
.get()
.build();
Call call = client.newCall(request);
这个 Builder 模式,说白了就是让你用“链式调用”一口气完成所有配置。为什么要这么设计?因为 OkHttpClient 和 Request 都有太多可选参数,如果都用构造方法重载,光看参数列表就得疯。Builder 模式把一个构造函数的“大爆炸”拆成了十几个方法调用,每个方法只做一件事,代码可读性瞬间提升了。
我个人习惯是:把 OkHttpClient 的建造抽取成方法,比如 createDefaultClient(),然后只在需要特殊配置的地方才加 Builder。这样团队里不会出现“一人一个超时时间”的混乱。
架构一张图看懂
下面这张图我特意画成了“厨房 → 菜单 → 骑手 → 送餐”的类比,方便你记忆。箭头表示调用/数据流向。
实战中容易踩的坑
我记得有一次帮同事定位线上问题——某个接口总是超时,抓包发现请求根本没发出去。查了半天,原来是他在 Builder 里给 OkHttpClient 配置了一个自定义 Dns,那个 Dns 解析回调里抛了异常,导致整个请求链断掉。你看,Builder 虽然方便,但每个环节都可能藏着陷阱。
- OkHttpClient 重用:每次请求都 new 一个 client,别这样。连接池、线程池全浪费了。
- Call 只能执行一次:
Call对象设计为单次执行。想重试?要么 newCall,要么用clone()。 - Builder 创建的 Request 是不可变对象:如果你拿到 Request 实例后想修改 URL,只能重新建一个 Builder。
一个小对比:OkHttp vs 其他网络库
| 维度 | OkHttp | Volley | Retrofit |
|---|---|---|---|
| 定位 | HTTP 客户端(底层) | 网络请求框架(含缓存、请求队列) | REST 接口封装(上层) |
| 连接复用 | ✅ 内置连接池 | ❌ 每次请求新建连接 | ✅ 基于 OkHttp |
| 自定义 | 极高(拦截器任意插拔) | 中 | 高(配合 OkHttp) |
| 学习成本 | 低(三个类) | 中 | 中(注解语法) |
我自己团队的项目现在都用 Retrofit + OkHttp 的组合。Retrofit 负责把接口定义成 Java 接口,OkHttp 负责真正的网络通信。但如果只是简单发个 GET/POST,直接用 OkHttp 就很舒服,没必要多套一层。
maxIdleConnections 调大一些,比如 10 个。我曾经在直播项目中调成 20,TCP 三次握手从每次请求 35ms 降到零,效果立竿见影。
好了,OkHttp 的三个核心概念就聊到这。下一个实战环节我们直接搭一个混合场景——用 OkHttp 发请求的同时,还要监听蓝牙 GATT 回调。这种场景下,线程模型和内存泄漏最容易出现,我们到时候细说。