OkHttp 是由 Square 公司开源的一个高性能 HTTP 客户端,是目前 Java 和 Android 生态中最主流、事实上的网络请求标准库。Android 官方从 Android 4.4 起底层已将 HttpURLConnection 替换为 OkHttp 实现,而当下流行的 Retrofit 网络框架底层也是交由 OkHttp 负责真正的网络收发。

相比 JDK 自带的 HttpURLConnection 或早期的 Apache HttpClient,OkHttp 具有显著的优势:

  1. 性能卓越:原生支持 HTTP/2 协议(多路复用)和连接池,能够共享同一个 Socket 连接,大幅降低 TCP 握手开销与请求延迟。
  2. 自动处理:支持透明的 GZIP 压缩(自动压缩请求体并解压响应体,节省流量)、响应缓存(避免重复网络请求)、以及自动处理重定向和故障恢复。
  3. API 简洁优雅:封装程度高,无需手动处理繁琐的流和状态码,代码量大幅减少,开发效率极高。
  4. 灵活的拦截器链:基于责任链设计模式,允许开发者轻松插拔日志记录、统一 Token 注入、签名校验、失败重试等功能。

一、整体架构

应用层
↓
OkHttpClient(配置中心:超时、拦截器、连接池)
↓
Dispatcher(异步请求调度器,维护 ready/running 队列)
↓
拦截器链(责任链模式)
↓
Server(真实网络请求)

二、拦截器链(最常考)

OkHttp 核心就是拦截器责任链,请求从上往下走,响应从下往上走:

应用拦截器(addInterceptor)
↓
RetryAndFollowUpInterceptor — 重试 & 重定向
↓
BridgeInterceptor — 补充请求头(Content-Type、Cookie、gzip)
↓
CacheInterceptor — 缓存处理(304、磁盘缓存)
↓
ConnectInterceptor — 建立 TCP 连接(连接池复用)
↓
网络拦截器(addNetworkInterceptor)
↓
CallServerInterceptor — 真正发送请求、读取响应

关键拦截器职责

拦截器 核心职责
RetryAndFollowUpInterceptor 失败重试、301/302 重定向、407 代理认证
BridgeInterceptor 补全 Content-Type、Accept-Encoding(gzip)、Cookie、User-Agent
CacheInterceptor 根据 Cache-Control / ETag / Last-Modified 判断缓存命中
ConnectInterceptor 从连接池取连接或新建,TLS 握手
CallServerInterceptor 写请求头+请求体,读响应头+响应体

三、连接池(ConnectionPool)

为什么需要连接池?

每次请求都建 TCP + TLS 握手太慢(约 1-3 秒),连接池复用已有连接。

默认配置:
maxIdleConnections = 5 // 最多空闲 5 个连接
keepAliveDuration = 5 min // 空闲超 5 分钟关闭

复用规则

  • 同一个 Address(host + port + proxy)可以复用
  • HTTP/2 可以多路复用(一个连接并发多个请求)
  • HTTP/1.1 默认一个连接一个请求(但连接可以排队复用)

清理机制

  • 后台线程每 5 分钟清理过期空闲连接
  • 引用计数:正在使用的连接不会被回收

四、缓存策略

// 客户端启用缓存
OkHttpClient client = new OkHttpClient.Builder()
.cache(new Cache(cacheDir, 10 * 1024 * 1024)) // 10MB
.build();

缓存判断流程

请求进来
→ 有缓存吗?
→ 没有 → 网络请求,存缓存
→ 有:
→ Cache-Control: no-cache / max-age 过期?
→ 是 → 发请求带 If-None-Match / If-Modified-Since
→ 304 → 用本地缓存
→ 200 → 更新缓存
→ 否 → 直接用本地缓存

关键响应头

头 作用
Cache-Control: max-age=3600 缓存有效期(秒)
Cache-Control: no-store 完全不缓存
Cache-Control: no-cache 每次验证
ETag + If-None-Match 资源指纹,304 则不传 body
Last-Modified + If-Modified-Since 按时间判断

五、重试与重定向

// RetryAndFollowUpInterceptor 核心逻辑
while (true) {
try {
response = proceed(request);
// 重定向处理
if (code == 301 || code == 302 || code == 307 || code == 308) {
request = buildRedirectRequest(response);
continue; // 重新走拦截器链
}
return response;
} catch (IOException e) {
if (retry(e)) continue;
throw e;
}
}
  • 默认最多重定向 20 次
  • 301/302:GET 重定向
  • 307/308:保持原请求方法重定向

六、调度器(Dispatcher)

// 异步请求管理
Dispatcher dispatcher = new Dispatcher();

dispatcher.maxRequests = 64; // 最大并发请求数
dispatcher.maxRequestsPerHost = 5; // 单个 host 最大并发

// 请求队列
readyAsyncCalls // 等待队列
runningAsyncCalls // 正在执行(HashMap)
runningSyncCalls // 同步请求(只记录,不限并发)

异步调度流程

  1. 请求放入 readyAsyncCalls
  2. 检查 runningAsyncCalls 数量 < 64 且该 host < 5
  3. 满足条件则移到 runningAsyncCalls,提交到线程池执行
  4. 线程池:ThreadPoolExecutor,核心线程 0,最大 64,空闲 60s 回收

七、OkHttp vs 其他方案

维度 OkHttp HttpURLConnection Retrofit
层级 底层 HTTP 客户端 JDK 原生 OkHttp 上层封装
连接池 内置 无 继承 OkHttp
拦截器 支持 不支持 支持
调试 EventListener 无 继承 OkHttp
使用场景 需要精细控制 简单请求 REST API 开发

八、常见问题

Q1: OkHttp 怎么添加公共参数?

// 应用拦截器(最常用)
client.addInterceptor(chain -> {
Request request = chain.request().newBuilder()
.addHeader("token", getToken())
.addQueryParam("version", "1.0")
.build();
return chain.proceed(request);
});

Q2: OkHttp 怎么取消请求?

Call call = client.newCall(request);
call.execute(); // 或 call.enqueue(...)

// 取消
call.cancel();
// 内部调用 RealCall.cancel() → 关闭 Socket

Q3: HTTPS 怎么配置证书固定(Certificate Pinning)?

CertificatePinner pinner = new CertificatePinner.Builder()
.add("api.example.com", "sha256/AAAA...=")
.build();

OkHttpClient client = new OkHttpClient.Builder()
.certificatePinner(pinner)
.build();

Q4: OkHttp 怎么处理 Cookie?

// 自动持久化
OkHttpClient client = new OkHttpClient.Builder()
.cookieJar(new CookieJar() {
private final Map<String, List<Cookie>> cookieStore = new HashMap<>();
@Override
public void saveFromResponse(HttpUrl url, List<Cookie> cookies) {
cookieStore.put(url.host(), cookies);
}
@Override
public List<Cookie> loadForRequest(HttpUrl url) {
return cookieStore.getOrDefault(url.host(), Collections.emptyList());
}
})
.build();

Q5: HTTP/2 相比 HTTP/1.1 的优势?

  • 多路复用:一个 TCP 连接并发多个请求(解决队头阻塞)
  • 头部压缩:HPACK 算法,减少重复 header 传输
  • 服务端推送:主动推送资源
  • 二进制分帧:解析更高效

Q6: OkHttp 的线程模型?

  • 同步 execute():当前线程阻塞
  • 异步 enqueue():回调在 Dispatcher 的线程池执行(非主线程)
  • 连接池清理:后台守护线程
  • 不需要手动切线程(但 UI 更新需切回主线程)

九、一句话总结

OkHttp 核心是拦截器责任链 + 连接池复用。面试重点:拦截器职责、连接池原理、缓存策略、线程模型。

Okhttp同时支持Http/3,但是:

  • 版本要求:需要使用Okhttp4.9以上版本。

  • 依赖引入:必须额外引入okhttp-quic依赖

  • 协议配置:需要在构建客户端时手动指定协议列表,例如:

    client = new OkHttpClient.Builder()
    .protocols(Arrays.asList(Protocol.HTTP_3, Protocol.HTTP_2, Protocol.HTTP_1_1))
    .build()

总结:OkHttp 开箱即用支持 HTTP/2;如果需要 HTTP/3,则需要升级版本、添加 QUIC 依赖并进行显式配置。