OkHttp 是由 Square 公司开源的一个高性能 HTTP 客户端,是目前 Java 和 Android 生态中最主流、事实上的网络请求标准库。Android 官方从 Android 4.4 起底层已将 HttpURLConnection 替换为 OkHttp 实现,而当下流行的 Retrofit 网络框架底层也是交由 OkHttp 负责真正的网络收发。
相比 JDK 自带的 HttpURLConnection 或早期的 Apache HttpClient,OkHttp 具有显著的优势:
- 性能卓越:原生支持 HTTP/2 协议(多路复用)和连接池,能够共享同一个 Socket 连接,大幅降低 TCP 握手开销与请求延迟。
- 自动处理:支持透明的 GZIP 压缩(自动压缩请求体并解压响应体,节省流量)、响应缓存(避免重复网络请求)、以及自动处理重定向和故障恢复。
- API 简洁优雅:封装程度高,无需手动处理繁琐的流和状态码,代码量大幅减少,开发效率极高。
- 灵活的拦截器链:基于责任链设计模式,允许开发者轻松插拔日志记录、统一 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)) .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 |
按时间判断 |
五、重试与重定向
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;
readyAsyncCalls runningAsyncCalls runningSyncCalls
|
异步调度流程
- 请求放入
readyAsyncCalls
- 检查
runningAsyncCalls 数量 < 64 且该 host < 5
- 满足条件则移到
runningAsyncCalls,提交到线程池执行
- 线程池:
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.cancel();
|
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,但是:
总结:OkHttp 开箱即用支持 HTTP/2;如果需要 HTTP/3,则需要升级版本、添加 QUIC 依赖并进行显式配置。