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 依赖并进行显式配置。