美文网首页
OkHttp网络框架4:拦截器 解析

OkHttp网络框架4:拦截器 解析

作者: xqiiitan | 来源:发表于2025-09-03 17:46 被阅读0次

OkHttp 拦截器。 责任链模式。

拦截器是OkHttp中提供的一种强大机制,它可以实现网络监听、请求以及响应重写、请求失败重试等功能。
拦截器不区分同步、 异步。

1.OkHttp core 系统内部提供的拦截器。

RetryAndFollowUpInterceptor 重试失败重定向拦截器。
↓ ↑
BridgeInterceptor 桥接适配拦截器
↓ ↑
CacheInterceptor 缓存拦截器
↓ ↑
ConnectInterceptor 连接拦截器,负责建立可用连接。
↓ ↑
CallServerInterceptor. http请求写入网络IO流,读取返回的数据。

2.拦截器的作用。

2.1 同步请求。

// RealCall.java
    client.dispatcher().executed(this); //加入同步请求队列中
    Response result = getResponseWithInterceptorChain();
    //执行完毕
    client.dispatcher().finished(this); //
    private Response getResponseWithInterceptorChain() throws IOException {
        List<Interceptor> interceptors = new ArrayList<>(); //创建一个拦截器列表
        interceptors.addAll(client.interceptors()); //优先处理自定义拦截器
        interceptors.add(retryAndFollowUpInterceptor); //失败重连拦截器
        interceptors.add(new BridgeInterceptor(client.cookieJar())); //接口桥接拦截器(同时处理cookie逻辑)
        
        interceptors.add(new CacheInterceptor(client.internalCache())); //缓存拦截器
        interceptors.add(new ConnectInterceptor(client)); //分配连接拦截器
        if (!retryAndFollowUpInterceptor.isForWebSocket()) { //web的socket连接的网络配置拦截器
          interceptors.addAll(client.networkInterceptors());
        }
        //最后是连接服务器发起真正的网络请求的拦截器
        interceptors.add(new CallServerInterceptor(
            retryAndFollowUpInterceptor.isForWebSocket())); 
            
        Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0, 
            originalRequest, this, eventListener, client.connectTimeoutMillis(),
            client.readTimeoutMillis(), client.writeTimeoutMillis());
        //流式执行并返回response
        return chain.proceed(originalRequest);
    }
//RealInterceptorChain.java
@Override public Response proceed(Request request) throws IOException {
    return proceed(request, streamAllocation, httpCodec, connection);
}
public Response proceed() {
    //核心代码,拦截器的链
    RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
        connection, index+1, request, call, eventListener, connectTimeout, readTimeout, writeTimeout);
    Interceptor interceptor = interceptor.get(index);
    Response response = interceptor.intercept(next);
}
//RetryAndFollowUpInterceptor.java
public Response intercept(Chain chain) { //【重点方法】
    Request request = chain.request();
    //...
    response = realChain.proceed(request, streamAllocation, null,null); 
    return response;        
}

拦截器总结1:
1.创建一系列拦截器,并将其放入一个拦截器list中;
2.创建一个拦截器链 RealInterceptorChain, 并执行拦截器的 proceed方法。

拦截器总结2:

  • 在发起请求前对request 进行处理;
  • 调用下一个拦截器,获取response;
  • 对response进行处理,返回给上一个拦截器。

3.每个拦截器的作用。

3.1 retryAndFollowUpInterceptor 失败重连拦截器

public Response intercept(Chain chain) { //【重点方法】
    Request request = chain.request();
    //获取连接服务端的connection,用于与服务端用于数据传输的输入输出流。最终传给ConnectInterceptor
    streamAllocation = new StreamAllocation(client.connectkionPool(), createAddress(request.url()),
        call, eventListener, callStackTrace );
    //...
    while(true){
        response = realChain.proceed(request, streamAllocation, null,null); 
        //...
        if(++followUpCount > MAX_FOLLOW_UPS) {//L168,不能无限制的重试网络。MAX_FOLLOW_UPS=20
            streamAllocation.release();
        }
    }
    return response;        
}

总结:

  • 创建 streamAllocation 对象
  • 调用 RealInterceptorChain.proceed() 进行网络请求
  • 根据异常结果或 响应结果判断是否要进行重新请求。
  • 调用下一个拦截器,对response进行处理,返回给上一个拦截器。

3.2 BridgeInterceptor

设置内容长度,编码方式,压缩解压缩, 添加头部信息等。

public Response intercept(Chain chain) { //【重点方法】
    Request userRequest = chain.request();
    Response networkResponse = chain.proceed(requestBuilder.build()); 
    
    //相当于获取解压后的数据
    GzipSource responseBody = new GzipSource();
    
    return responseBuilder.build();
}

总结:

  • 负责将用户构建的一个Request 请求转换为能够进行网络访问的请求。
  • 将这个符合网络请求的Request 进行网络请求;
  • 将网络请求回来的Response 转化为用户可用的Response。

3.3 CacheInterceptor 缓存拦截器

要使用这个功能要给OkHttpClient 设置cache。
new OkHttpClient.Builder()
.cache(new Cache(new File("cache"), 1010241024))
.build();

// Cache.java
CacheRequest put(Response response){
    String requestMethod = response.request().method();
    if(!requestMtthod.equals("GET")) return null; // 不是Get请求 返回null。
    
    Entry entry = new Entry(response); //Entry用于包装缓存的信息
    DiskLruCache.Editor editor = cache.edit(key(response.request().url()));
    entry.writeTo(editor); //缓存写到磁盘上
    return new CacheRequestImpl(editor); //CacheRequestImpl的body是响应主体。数据缓存。
}
Response get(Request request) { //从缓存中读取response。
    String key = key(request.url()); //key用于解密   
    DiskLruCache.Snapshot snapshot = cache.get(key); //获得缓存的值
    Entry entry = new Entry(snapshot.getSource(ENTRY_METADATA));
    Response response = entry.response(snapshot);
    //是否匹配,不匹配返回null
    if(!entry.matches(request, response)) {
        Util.closeQuitely(response.body());
        return null;
    }
    return response(response)
}
// 缓存拦截器 CacheInterceptor.java
@Override public Response intercept(Chain chain) throws IOException {
    Response cacheCandidator = cache!=null ? cache.get(chain.request()) :null;
    CacheStrategy strategy = new CacheStrategy.Factory(now,chain.request(), cacheCandidate).get();
    Request networkRequest = strategy.networkRequest;
    Response cacheResponse = strategy.cacheResponse;
    
    // 没网络,且没缓存,构建一个504错误。
    // 没网络,直接返回网络缓存结果。
    Response networkResponse = chiain.proceed(networkRequest);
    // 304从缓存中读取数据。
    
    if(HttpHeaders.hadBody(response) && CacheStrategy.isCacheable(...)) {
        CacheRequest cacheRequest = cache.put(response);
        return cacheWritingResponse(cacheRequest, response);
    }
    if(HttpMethod.invalidatesCache(networkRequest.method())) {
        cache.remove(networkRequest);
    }
}
//CacheStrategy.java
get()--> getCandidate()-->
Response.Builder build = cacheResponse.newBuilder();
return new CacheStrategy(null, builder.build());

3.4 连接池 ,拦截器 ConnectInterceptor

打开与 服务器之间的连接,正式开启网络请求。
ConnectInterceptor(client)

@Override public Response intercept(Chain chain) throws IOException {
    RealChain realChain = (RealInterceptorChain) chain;
    Request request = realChain.request();
    StreamAllocation streamAllocation = realChain.streamAllocation();//获取前面的变量,来自重试重定向拦截器
    
    boolean doExtensiveHealthChecks = !request.method().equals("GET");
    //HttpCodec 处理请求和返回数据。
    HttpCodec httpCodec = streamAllocation.netStream(client,chain, doExtensiveHealthChecks);
    RealConnection connection = streamAllocation.connection();//【关键对象】做实际的网络传输 
    
    return realChain.proceed(request,streamAllocation, httpCodec, connection);
}

总结:

  • ConnectInterceptor获取Interceptor传过来的 StreamAllocation,StreamAllocation.newStream() 得到HttpCodec。
  • 将刚才创建的用于网络IO的RealConnection对象, 以及对于与服务器交互最为关键的 HttpCodec等对象传递给后面的拦截器.

//newStream()方法。

public HttpCodec new Stream() {
    
    RealConnection resultConnection = findHeallthyConnection();// findConnection()
    HttpCodec resultCodec = resultConnection.newCodec(client, chain,this);
    return resultCodec;
}
findConnection()作用。
1. 尝试获取connection,如果可以复用就复用;不能复用就从连接池获取新的RealConnection连接。
2. 将新的连接,放到连接池中。
    okhttp中连接方式分类:Tunnel 隧道连接、Socket连接。

3.4.1 连接池的操作。ConnectionPool 维护网络连接,管理连接的复用。

在一定时间内,复用连接。能有效的清理回收

//ConnectionPool.java
RealConnection get(Address address, StreamAllocation streamAllocation) {
    for(RealConnectioin connection: connections) {
        if(connection.isEligible(address, route)) { //判断连接是否可用
            streamAllocation.acquire(connection, true);//获取连接
            return connection;
        }
    }
    return null;
}
public void acquire(RealConnection connection, boolean reportAcquired) {
    this.connection =connection;
    this.reportAcquired =reportAcquired;
    connection.allocations.add(new StreamAllocationReference(this, callStackTrace));
    //用于判断集合的大小,根据集合大小,来判断每个连接是否超过了最大连接数。
}
// put方法。
void put(RealConnection connection) {
    if(!cleanupRunning) {
        cleanupRunning = true;
        executor.execute(cleanupRunning);//回收connection时使用。
    }
    connections.add(connection); // 加入队列中
}

总结:

  • 每次请求,都会在重试重定向拦截器,产生一个StreamAllocation对象;
  • StreamAllocation对象 的弱引用,添加到RealConnection 对象的 allocations 集合中。
  • 从连接池中获取,来复用。

3.4.1 自动回收 connection

Gc的回收算法,统计StreamAllocation的数量。
有个独立的线程 cleanUpRunnable 线程。

// ConnectionPool.java
private final Runnable cleanUpRunnable = new Runnable(0 {
    @Override public void run(0 {
        while(true) {
            long waitNanos = cleanup(System.nanoTime());//GC回收算法,标记清除算法
            if(waitNanos == -1) return;
            if(waitNanos > 0) {
                 long waitMillis = waitNanos/1000000L;
                 waitNanos -= (waitMillis *1000000L);   
                 synchronized (ConnectionPool.java) {
                    ConnectionPool.this.wait(waitMillis, (int)waitNanos);
                 }
            }
        }
    }
}

// 后续方法:
pruneAndGetAllocationCount();

总结:

  • okHttp使用了GC回收算法;
  • StreamAllocation 的数量会渐渐变成0;
  • 被线程池检测到并回收,这样就可以保持多个健康的keep-alive 连接。

3.5 CallServerInterceptor

1.真正服务器发起请求。2.接收到服务器给我们的响应。再返回。

@Override public Response intercept(Chain chain) throws IOException {
    RealInterceptorChain realChain =(RealInterceptorChain)chain;//链工具
    HttpCodec httpCodec =realChain.httpStream();//编码Request,解码response输出
    StreamAllocation streamAllocation = realChain.streamAllocation();//分配stream
    RealConnection connection =(RealConnection)realChain.connection();//抽象链接的具体实现
    Request request = realChain.request();//网络请求
        
    httpCodec.writeRequestHeaders(request);//1.写入请求头部header信息
    request.body().writeTo(bufferedRequestBody); //2.写入请求的body信息。   
    httpCodec.finishRequest(); //3.完成了网络请求的写入工作。
    
    //读取工作。
    responseBuilder = httpCodec.readResponseHeaders(false);//4.读取网络响应的头部信息
    response = response.newBuilder()
            .body(httpCodec.openResponseBody(response)).build();//5.读取网络响应的body信息
    //禁止新的流创建,StreamAllocation.noNewsStreams();
    
    return response;//完成response的获取工作。
}

okhttp一次网络请求的大致过程

  • Call对象对请求的封装。
  • Dispatcher对请求的分发
  • getResponseWithInterceptors方法,这里面是拦截器链,里面有几个拦截器。
    • RetryAndFollowUpInterceptor 负责重试和重定向请求;
    • CacheInterceptor 处理缓存的拦截器;
    • BridgeInterceptor 负责okHttp 请求和响应对象 与实际http协议当中的 请求和响应对象的转换。
    • ConnectionInterceptor 负责建立连接,和流对象的。
    • CallServerInterceptor 负责完成最终的网络请求。发送请求+读取响应。

----------End------------------------

相关文章

网友评论

      本文标题:OkHttp网络框架4:拦截器 解析

      本文链接:https://www.haomeiwen.com/subject/mecvajtx.html