第三章:Okhttp 拦截器链深度解析

138 阅读6分钟

3.1 拦截器链整体架构

3.1.1 责任链模式在OkHttp中的应用

OkHttp通过拦截器链实现了高度模块化的请求处理流程:

deepseek_mermaid_20250717_1dfefa.png

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响应)

执行流程

deepseek_mermaid_20250717_decd69.png 关键源码

// 源码路径: 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:缓存处理

缓存决策流程

deepseek_mermaid_20250717_26efed.png

关键源码

// 源码路径: 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

核心功能

  • 写入请求头和请求体
  • 读取响应头和响应体
  • 处理分块传输编码
  • 关闭连接资源

请求写入流程

deepseek_mermaid_20250717_e617db.png

关键源码

// 源码路径: 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);
}

本章小结

  1. 拦截器链架构

    • 采用责任链模式处理请求
    • 内置7个核心拦截器
    • 支持自定义拦截器扩展
  2. 核心拦截器功能

    • RetryAndFollowUpInterceptor:处理重试和重定向
    • BridgeInterceptor:添加必要请求头
    • CacheInterceptor:实现HTTP缓存
    • ConnectInterceptor:管理网络连接
    • CallServerInterceptor:处理网络I/O
  3. 拦截器类型

    • 应用拦截器:最先执行,适合全局处理
    • 网络拦截器:在网络层执行,适合监控
  4. 最佳实践

    • 避免在拦截器中执行阻塞操作
    • 精简拦截器逻辑,保持单一职责
    • 区分应用拦截器和网络拦截器的使用场景

在下一章中,我们将深入分析OkHttp的连接管理机制,包括连接池的工作原理、TCP连接建立过程、以及HTTP/2的多路复用实现