1. 整体架构解析:责任链模式、注册顺序与请求流转
1.1 原理:责任链模式与 Interceptor 接口
OkHttp 的拦截器链基于经典责任链模式(Chain of Responsibility)设计。在该模式下,每个拦截器独立处理请求/响应,并决定是否将任务传递给链中的下一个节点。这种设计让网络层具备高度可插拔性——增删一个拦截器不影响其他环节。
核心接口定义:
Interceptor 是链中的基本单元,Chain 负责维护链式调用状态。
// Interceptor 接口(OkHttp 4.x / OkHttp 3.x 相同)
public interface Interceptor {
Response intercept(Chain chain) throws IOException;
interface Chain {
Request request();
Response proceed(Request request) throws IOException;
Connection connection();
Call call();
int connectTimeoutMillis();
Chain withConnectTimeout(int timeout, TimeUnit unit);
int readTimeoutMillis();
Chain withReadTimeout(int timeout, TimeUnit unit);
int writeTimeoutMillis();
Chain withWriteTimeout(int timeout, TimeUnit unit);
}
}
每个拦截器在 intercept() 中既可以预处理请求(添加头、修改 body),也可以后处理响应(解压缩、缓存写入)。关键规则:必须调用 chain.proceed(request) 才能将请求传递给下一拦截器,否则链会中断。
1.2 拦截器注册顺序与分类
OkHttp 支持两类拦截器,注册 API 不同,在链中的位置也不同:
| 拦截器类型 | 注册方法 | 链中位置 | 典型用途 |
|---|---|---|---|
| 应用层拦截器 | addInterceptor() | 位于 RetryAndFollowUpInterceptor 之前 | 统一加签、日志、请求头注入 |
| 网络层拦截器 | addNetworkInterceptor() | 位于 ConnectInterceptor 之后,靠近实际网络 I/O | 重试、重定向、网络级监控 |
内置拦截器的固定注册顺序(不可调换)如下表所示,该顺序决定了请求/响应的处理流水线:
| 序号 | 拦截器 | 职责 | 是否可跳过 |
|---|---|---|---|
| 1 | 应用层拦截器(用户自定义) | 全局预处理/后处理,如统一 Header、请求加密 | —(用户控制) |
| 2 | RetryAndFollowUpInterceptor | 连接失败重试、3xx 重定向跟随 | 否(核心稳定) |
| 3 | BridgeInterceptor | 补充请求头(Content-Type、Content-Length、Host 等),解压 gzip | 否 |
| 4 | CacheInterceptor | 缓存命中直接返回,未命中写缓存 | 可移除 |
| 5 | ConnectInterceptor | 建立 TCP / TLS 连接,复用连接池 | 否 |
| 6 | 网络层拦截器(用户自定义) | 网络级监控、Mock、网络日志 | —(用户控制) |
实践建议:应用层拦截器适合做业务语义的处理(如 刷新、参数签名);网络层拦截器适合做网络诊断(如抓包、模拟弱网)。不要将业务逻辑混入网络层拦截器,否则重试时会重复执行。
1.3 请求与响应流转过程
一次完整的 HTTP 请求在拦截器链中经历两次贯穿:
- 请求下行(Request → Server):从链首逐层穿透到链尾,每个拦截器可修改请求、添加 header、重试逻辑。
- 响应上行(Server → Request):从链尾逐层回溯到链首,每个拦截器可处理响应、解压缩、缓存写入。
流转的核心是 RealInterceptorChain 的 proceed() 方法,它通过递归/迭代索引驱动链前进:
// RealInterceptorChain.proceed() 简化逻辑
public Response proceed(Request request, StreamAllocation streamAllocation, ...) throws IOException {
if (index >= interceptors.size()) throw new AssertionError();
calls++;
// 创建下一个链节点,index + 1
RealInterceptorChain next = new RealInterceptorChain(
interceptors, streamAllocation, ... , index + 1, request);
// 获取当前拦截器并执行
Interceptor interceptor = interceptors.get(index);
Response response = interceptor.intercept(next);
return response;
}
每个拦截器调用 chain.proceed(request) 时,链索引 +1,递归进入下一拦截器;当所有拦截器执行完毕,proceed() 返回最终响应,再逐层回溯。
常见错误:如果在
intercept() 中没有调用 chain.proceed(),请求将永远无法到达服务器,且链中后续拦截器全部失效。此时 intercept() 必须自己构造并返回一个 Response 对象,否则抛出 NullPointerException。
1.4 核心 API 与配置示例
构建 OkHttpClient 时注册拦截器:
// 应用层拦截器
Interceptor appInterceptor = new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
Request original = chain.request();
// 预处理:添加
Request modified = original.newBuilder()
.header("Authorization", "Bearer " + )
.build();
// 传递给下一拦截器
Response response = chain.proceed(modified);
// 后处理:记录响应状态
return response;
}
};
// 网络层拦截器
Interceptor netInterceptor = new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
// 仅记录网络往返耗时
long start = System.nanoTime();
Response response = chain.proceed(chain.request());
long elapsed = System.nanoTime() - start;
System.out.println("Network round-trip: " + elapsed / 1_000_000 + " ms");
return response;
}
};
// 注册到 OkHttpClient
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(appInterceptor) // 应用层
.addNetworkInterceptor(netInterceptor) // 网络层
.build();
调试技巧:在开发阶段,使用
HttpLoggingInterceptor(需引入 logging-interceptor 依赖)打印完整的请求/响应报文。设置 level = BODY 可看到 header 与 body 细节。
// 引入依赖:implementation("com.squareup.okhttp3:logging-interceptor:4.12.0")
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(logging)
.build();
1.5 常见错误与避坑
| 错误场景 | 现象 | 根因 | 解决方案 |
|---|---|---|---|
| 拦截器未执行 | 自定义逻辑完全没生效 | 拦截器未通过 addInterceptor() / addNetworkInterceptor() 注册 | 检查 Builder 链中是否添加 |
| 请求卡死 / 超时 | 请求无响应,最终抛 SocketTimeoutException | 拦截器内未调用 chain.proceed() | 确保每个拦截器都调用了 proceed() 或返回合法 Response |
| 响应数据异常 | 响应体为空或乱码 | 拦截器多次调用 proceed() 导致流被消费 | 每个拦截器只调用一次 proceed(),缓存 Response body |
| 顺序不符合预期 | 应用层逻辑在重试后未再次执行 | 把业务逻辑放在了网络层拦截器 | 业务逻辑应放在应用层拦截器,网络层只做网络级处理 |
1.6 性能与稳定性优化要点
- 避免耗时操作:拦截器运行在 OkHttp 的调度线程中,不要做数据库读写、大规模 JSON 解析、同步网络请求。必要时应使用异步回调或切换线程。
- 控制拦截器数量:每增加一个拦截器就多一次方法调用和对象创建。生产环境建议应用层拦截器 ≤ 3 个,网络层拦截器 ≤ 2 个。
- 复用连接池:
ConnectInterceptor依赖ConnectionPool,自定义拦截器不要关闭或污染连接,避免连接泄漏。 - 缓存策略:
CacheInterceptor配合Cache对象可大幅减少重复请求。设置合理的maxAge和maxStale避免缓存雪崩。 - 监控与熔断:在网络层拦截器中集成失败率监控,当错误率超过阈值时快速失败(断路器模式),防止下游故障拖垮客户端。