3.1 拦截器链整体架构
3.1.1 责任链模式在OkHttp中的应用
OkHttp通过拦截器链实现了高度模块化的请求处理流程:
3.1.2 拦截器链构建源码分析
// 源码路径: okhttp3/RealCall.java
Response getResponseWithInterceptorChain() throws IOException {
// 构建完整的拦截器列表
List<Interceptor> interceptors = new ArrayList<>();
// 1. 用户自定义的应用拦截器(最先执行)
interceptors.addAll(client.interceptors());
// 2. 内置核心拦截器
interceptors.add(new RetryAndFollowUpInterceptor(client));
interceptors.add(new BridgeInterceptor(client.cookieJar()));
interceptors.add(new CacheInterceptor(client.internalCache()));
interceptors.add(new ConnectInterceptor(client));
// 3. 用户自定义的网络拦截器
if (!forWebSocket) {
interceptors.addAll(client.networkInterceptors());
}
// 4. 最终的网络请求拦截器
interceptors.add(new CallServerInterceptor(forWebSocket));
// 创建拦截器链
Interceptor.Chain chain = new RealInterceptorChain(
interceptors,
transmitter,
null,
0,
originalRequest
);
// 启动责任链
return chain.proceed(originalRequest);
}
3.2 核心拦截器详解
3.2.1 RetryAndFollowUpInterceptor:重试与重定向
核心功能:
- 处理连接失败的重试
- 处理HTTP重定向(3xx响应)
- 处理授权挑战(401/407响应)
执行流程:
关键源码:
// 源码路径: okhttp3/internal/http/RetryAndFollowUpInterceptor.java
public Response intercept(Chain chain) throws IOException {
int followUpCount = 0;
Response priorResponse = null;
while (true) {
// 尝试请求
Response response = null;
try {
response = ((RealInterceptorChain) chain).proceed(request);
} catch (RouteException e) {
// 路由异常:尝试恢复
if (!recover(e.getLastConnectException(), false, request)) {
throw e.getLastConnectException();
}
continue;
}
// 检查是否需要重定向或重试
Request followUp = followUpRequest(response, priorResponse);
if (followUp == null) {
return response; // 不需要重定向
}
// 检查重定向次数限制
if (++followUpCount > MAX_FOLLOW_UPS) {
throw new ProtocolException("Too many follow-up requests: " + followUpCount);
}
request = followUp;
priorResponse = response;
}
}
3.2.2 BridgeInterceptor:请求桥接
核心功能:
- 添加必要请求头(User-Agent, Host, Connection等)
- 处理Gzip压缩
- 处理Cookie管理
请求头添加逻辑:
// 源码路径: okhttp3/internal/http/BridgeInterceptor.java
public Response intercept(Chain chain) throws IOException {
Request userRequest = chain.request();
Request.Builder requestBuilder = userRequest.newBuilder();
// 1. 添加内容类型
RequestBody body = userRequest.body();
if (body != null) {
MediaType contentType = body.contentType();
if (contentType != null) {
requestBuilder.header("Content-Type", contentType.toString());
}
// 2. 添加内容长度
long contentLength = body.contentLength();
if (contentLength != -1) {
requestBuilder.header("Content-Length", Long.toString(contentLength));
requestBuilder.removeHeader("Transfer-Encoding");
} else {
requestBuilder.header("Transfer-Encoding", "chunked");
requestBuilder.removeHeader("Content-Length");
}
}
// 3. 添加默认请求头
if (userRequest.header("Host") == null) {
requestBuilder.header("Host", hostHeader(userRequest.url(), false));
}
if (userRequest.header("Connection") == null) {
requestBuilder.header("Connection", "Keep-Alive");
}
if (userRequest.header("Accept-Encoding") == null) {
requestBuilder.header("Accept-Encoding", "gzip");
}
// 4. 处理Cookie
List<Cookie> cookies = cookieJar.loadForRequest(userRequest.url());
if (!cookies.isEmpty()) {
requestBuilder.header("Cookie", cookieHeader(cookies));
}
// 5. 执行请求
Response networkResponse = chain.proceed(requestBuilder.build());
// 6. 处理Gzip压缩响应
return unzip(networkResponse);
}
3.2.3 CacheInterceptor:缓存处理
缓存决策流程:
关键源码:
// 源码路径: okhttp3/internal/cache/CacheInterceptor.java
public Response intercept(Chain chain) throws IOException {
// 1. 尝试获取缓存
CacheStrategy strategy = new CacheStrategy.Factory(
System.currentTimeMillis(),
chain.request(),
cacheCandidate
).get();
// 2. 无网络请求且缓存可用
if (strategy.networkRequest == null && strategy.cacheResponse != null) {
return strategy.cacheResponse.newBuilder()
.cacheResponse(stripBody(strategy.cacheResponse))
.build();
}
// 3. 执行网络请求
Response networkResponse = null;
try {
networkResponse = chain.proceed(strategy.networkRequest);
} finally {
// 关闭资源
}
// 4. 处理304响应(缓存有效)
if (cacheResponse != null) {
if (networkResponse.code() == HTTP_NOT_MODIFIED) {
Response response = cacheResponse.newBuilder()
.headers(combine(cacheResponse.headers(), networkResponse.headers()))
.build();
cache.update(cacheResponse, response);
return response;
}
}
// 5. 构建最终响应
Response response = networkResponse.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.networkResponse(stripBody(networkResponse))
.build();
// 6. 缓存新响应
if (cache != null) {
if (CacheStrategy.isCacheable(response, networkRequest)) {
CacheRequest cacheRequest = cache.put(response);
return cacheWritingResponse(cacheRequest, response);
}
}
return response;
}
3.2.4 ConnectInterceptor:连接管理
核心职责:
- 建立或复用TCP连接
- 处理TLS/SSL握手
- 协议协商(HTTP/1.1或HTTP/2)
关键源码:
// 源码路径: okhttp3/internal/http/ConnectInterceptor.java
public Response intercept(Chain chain) throws IOException {
RealInterceptorChain realChain = (RealInterceptorChain) chain;
Request request = realChain.request();
// 获取流分配对象
StreamAllocation streamAllocation = realChain.streamAllocation();
// 建立连接(或复用)
HttpCodec httpCodec = streamAllocation.newStream(
client,
chain,
doExtensiveHealthChecks
);
RealConnection connection = streamAllocation.connection();
// 继续执行下一个拦截器
return realChain.proceed(request, streamAllocation, httpCodec, connection);
}
3.2.5 CallServerInterceptor:网络I/O
核心功能:
- 写入请求头和请求体
- 读取响应头和响应体
- 处理分块传输编码
- 关闭连接资源
请求写入流程:
关键源码:
// 源码路径: okhttp3/internal/http/CallServerInterceptor.java
public Response intercept(Chain chain) throws IOException {
// 获取HTTP编解码器
HttpCodec httpCodec = ((RealInterceptorChain) chain).httpStream();
// 1. 写入请求头
httpCodec.writeRequestHeaders(request);
// 2. 处理请求体(如果有)
if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) {
Sink requestBodyOut = httpCodec.createRequestBody(request, request.body().contentLength());
BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);
request.body().writeTo(bufferedRequestBody);
bufferedRequestBody.close();
}
// 3. 完成请求
httpCodec.finishRequest();
// 4. 读取响应头
Response response = httpCodec.readResponseHeaders(false).request(request)
.handshake(streamAllocation.connection().handshake())
.build();
// 5. 读取响应体
if (forWebSocket && response.code() == 101) {
// WebSocket特殊处理
} else {
response = response.newBuilder()
.body(httpCodec.openResponseBody(response))
.build();
}
return response;
}
3.3 自定义拦截器开发
3.3.1 应用拦截器 vs 网络拦截器
| 特性 | 应用拦截器 | 网络拦截器 |
|---|---|---|
| 添加位置 | OkHttpClient.Builder.addInterceptor() | OkHttpClient.Builder.addNetworkInterceptor() |
| 执行顺序 | 最先执行,最后收到响应 | 在ConnectInterceptor之后执行 |
| 调用次数 | 每个请求只调用一次 | 可能因重定向/重试调用多次 |
| 可见内容 | 原始请求,未添加必要头 | 完整网络请求,包含添加的头 |
| 适用场景 | 日志记录、请求修改 | 网络层监控、重试策略 |
3.3.2 日志拦截器示例
public class LoggingInterceptor implements Interceptor {
private static final String TAG = "OkHttp";
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
// 记录请求信息
long startTime = System.nanoTime();
Log.d(TAG, String.format("Sending request %s on %s%n%s",
request.url(), chain.connection(), request.headers()));
// 执行请求
Response response = chain.proceed(request);
// 记录响应信息
long endTime = System.nanoTime();
Log.d(TAG, String.format("Received response for %s in %.1fms%n%s",
response.request().url(), (endTime - startTime) / 1e6d, response.headers()));
return response;
}
}
3.3.3 认证拦截器示例
public class AuthInterceptor implements Interceptor {
private final TokenManager tokenManager;
public AuthInterceptor(TokenManager tokenManager) {
this.tokenManager = tokenManager;
}
@Override
public Response intercept(Chain chain) throws IOException {
Request originalRequest = chain.request();
// 添加认证头
Request authenticatedRequest = originalRequest.newBuilder()
.header("Authorization", "Bearer " + tokenManager.getToken())
.build();
// 执行请求
Response response = chain.proceed(authenticatedRequest);
// 处理401未授权
if (response.code() == 401) {
synchronized (this) {
// 刷新令牌
String newToken = tokenManager.refreshToken();
// 使用新令牌重试
Request newRequest = originalRequest.newBuilder()
.header("Authorization", "Bearer " + newToken)
.build();
return chain.proceed(newRequest);
}
}
return response;
}
}
3.4 拦截器链高级特性
3.4.1 拦截器执行顺序控制
OkHttpClient client = new OkHttpClient.Builder()
// 添加顺序:1 -> 2 -> 3
.addInterceptor(new Interceptor1())
.addInterceptor(new Interceptor2())
.addInterceptor(new Interceptor3())
.build();
实际执行顺序:
Request
↓
Interceptor1 (前置处理)
↓
Interceptor2 (前置处理)
↓
Interceptor3 (前置处理)
↓
Network
↓
Interceptor3 (后置处理)
↓
Interceptor2 (后置处理)
↓
Interceptor1 (后置处理)
↓
Response
3.4.2 网络拦截器访问网络层信息
public class NetworkMonitorInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
// 获取网络层信息
Connection connection = chain.connection();
Route route = connection != null ? connection.route() : null;
Proxy proxy = route != null ? route.proxy() : null;
// 记录网络信息
logNetworkInfo(connection, route, proxy);
// 执行请求
Response response = chain.proceed(chain.request());
// 记录响应时间
logResponseTime(response);
return response;
}
}
3.5 拦截器性能优化
3.5.1 避免阻塞操作
// 错误示例:在拦截器中执行阻塞IO
public class BlockingInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
// 阻塞操作(网络请求/数据库访问)
User user = fetchUserFromDB(); // 可能阻塞线程
Request request = chain.request().newBuilder()
.header("User-Id", user.getId())
.build();
return chain.proceed(request);
}
}
// 正确做法:预加载数据或异步处理
public class AsyncInterceptor implements Interceptor {
private final String userId;
public AsyncInterceptor(String userId) {
this.userId = userId; // 提前获取
}
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request().newBuilder()
.header("User-Id", userId)
.build();
return chain.proceed(request);
}
}
3.5.2 精简拦截器逻辑
// 优化前:复杂处理逻辑
public Response intercept(Chain chain) throws IOException {
// 复杂的数据处理
processData(chain.request());
// 多个网络请求
fetchAdditionalData();
// 数据库操作
saveToDatabase();
return chain.proceed(chain.request());
}
// 优化后:精简拦截器职责
public Response intercept(Chain chain) throws IOException {
// 只做必要的请求修改
Request modified = simpleModification(chain.request());
return chain.proceed(modified);
}
本章小结
-
拦截器链架构:
- 采用责任链模式处理请求
- 内置7个核心拦截器
- 支持自定义拦截器扩展
-
核心拦截器功能:
RetryAndFollowUpInterceptor:处理重试和重定向BridgeInterceptor:添加必要请求头CacheInterceptor:实现HTTP缓存ConnectInterceptor:管理网络连接CallServerInterceptor:处理网络I/O
-
拦截器类型:
- 应用拦截器:最先执行,适合全局处理
- 网络拦截器:在网络层执行,适合监控
-
最佳实践:
- 避免在拦截器中执行阻塞操作
- 精简拦截器逻辑,保持单一职责
- 区分应用拦截器和网络拦截器的使用场景
在下一章中,我们将深入分析OkHttp的连接管理机制,包括连接池的工作原理、TCP连接建立过程、以及HTTP/2的多路复用实现