深入理解OkHttp3(1):Interceptors

Version : okhttp:3.6.0

Interceptors是OkHttp3整个框架的核心,包含了请求监控、请求重写、调用重试等机制。它主要使用责任链模式,解决请求与请求处理之间的耦合。 img

1.1 责任链模式

将接收对象放入链中,按照链中顺序让多个对象处理请求。请求者不用知道具体是由谁处理。解决请求与接收之间的耦合,提高灵活性。 责任链负责对请求参数的解析,所有的扩展都是针对链中节点进行扩展。

OkHttp3的Interceptors的责任链 img

1.2 OkHttp3链式流程

img

2.1 RetryAndFollowUpInterceptor

RetryAndFollowUpInterceptor用于尝试恢复失败和重定向的请求,最多支持跟踪20次重定向。创建streamAllocation维护请求的Connections、Streams、Calls,类似中介者模式,之后交给BridgeInterceptor节点处理请求。

@Override public Response intercept(Chain chain) throws IOException {
    ...
    // 创建用于协调Connections、Streams、Call三者关系的streamAllocation
    streamAllocation = new StreamAllocation(
        client.connectionPool(), createAddress(request.url()), callStackTrace);
    // 重定向次数
    int followUpCount = 0;
    Response priorResponse = null;
    while (true) {
      // 无限循环
      ...
      Response response = null;
      boolean releaseConnection = true;
      try {
        response = ((RealInterceptorChain) chain).proceed(request, streamAllocation, null, null);
        releaseConnection = false;
      } catch (RouteException e) {
        // 连接路由失败,请求未发送
        ...
        releaseConnection = false;
        continue;
      } catch (IOException e) {
        // 与服务端通信失败,请求已发送
        ...
        releaseConnection = false;
        continue;
      } finally {
        // 释放资源
        if (releaseConnection) {
          ...
        }
      }
      // 记录上一次的响应,一般出现在重定向情况时。
      ...
      // 判断是否是重定向的响应
      Request followUp = followUpRequest(response);
      ...
      if (followUp == null) {
        ...
        // 正常响应直接返回
        return response;
      }
      ...
      // 检查是否能够继续重定向操作
      ...
      request = followUp;
      priorResponse = response;
    }
  }

RetryAndFollowUpInterceptor处理下层链中节点返回的响应和抛出的异常。 依据返回的响应或抛出的异常,进行检查和恢复操作

  1. 关闭已建立的Socket连接
  2. OkHttpClient是否关闭重连,默认开启重连
  3. 请求是否已发送并且请求体不可重读,不可重连
  4. 出现的致命的异常:请求协议异常、证书验证异常等
  5. 是否有下一跳可尝试的路由。
private boolean recover(IOException e, boolean requestSendStarted, Request userRequest) {
    // 关闭Socket
    streamAllocation.streamFailed(e);
    // 如果Application层禁止重连,则直接失败
    if (!client.retryOnConnectionFailure()) return false;
    // 是否可以再次发送请求
    if (requestSendStarted && userRequest.body() instanceof UnrepeatableRequestBody) return false;
    // 致命异常则不可恢复
    if (!isRecoverable(e, requestSendStarted)) return false;
    // 没有可以再次尝试的路由
    if (!streamAllocation.hasMoreRoutes()) return false;
    return true;
  }

2.2 BridgeInterceptor

BridgeInterceptor是应用层与网络层节点的桥接,补全应用层请求的头部信息,调用之后网络与缓存数据处理,最后将响应返回给上层。

@Override public Response intercept(Chain chain) throws IOException {
    // 重写请求头部,填充必要的头部信息
    ...
    // 添加 "Accept-Encoding: gzip" header ,可以压缩请求数据
    ...
     requestBuilder.header("Accept-Encoding", "gzip");
    ...

    // 配置Cookie和代理信息
    ...
    requestBuilder.header("Cookie", cookieHeader(cookies));
    ...
    requestBuilder.header("User-Agent", Version.userAgent());
    ...
    // 把新构建的请求向下传递处理
    Response networkResponse = chain.proceed(requestBuilder.build());
    // 处理下层节点返回的响应,响应可能是缓存或者网络数据
    HttpHeaders.receiveHeaders(cookieJar, userRequest.url(), networkResponse.headers());

    Response.Builder responseBuilder = networkResponse.newBuilder()
        .request(userRequest);
    // 响应数据解压
    ...
    return responseBuilder.build();
  }

2.3 CacheInterceptor

本地缓存和网络缓存,默认无缓存。

  1. 读取本地缓存,根据请求缓存策略构建网络请求和缓存响应。
  2. 按照请求缓存策略,返回缓存或传递给ConnectInterceptor执行下一步数据操作。
  3. 处理返回的网络响应数据的缓存操作。
@Override public Response intercept(Chain chain) throws IOException {
    // 读取本地磁盘缓存
    Response cacheCandidate = 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,请求失败
    if (networkRequest == null && cacheResponse == null) {
      ...
      return ...;
    }

    // 禁止网络请求,缓存存在,返回响应到上一级
    if (networkRequest == null) {
      // return Cache
      ...
    }

    Response networkResponse = null;
    try {
      networkResponse = chain.proceed(networkRequest);
    } finally {
      // 处理网络缓存失败时,释放缓存流
    }

    // 本地存在缓存则检查响应状态码是否为304
    if (cacheResponse != null) {
      if (networkResponse.code() == HTTP_NOT_MODIFIED) {
        // update cache
        ...
        return response;
      } else {
        // close cacheResponse 
      }
    }
   
    // 构建新响应
    Response response = networkResponse.newBuilder()
        .cacheResponse(stripBody(cacheResponse))
        .networkResponse(stripBody(networkResponse))
        .build();

    if (HttpHeaders.hasBody(response)) {
      // 响应缓存
      CacheRequest cacheRequest = maybeCache(response, networkResponse.request(), cache);
      response = cacheWritingResponse(cacheRequest, response);
    }

    return response;
  }

2.4 ConnectInterceptor

  1. 创建网络读写流必要的HttpCodec,用于请求编码和网络响应解码处理。
  2. 复用或建立Socket连接RealConnection,用于网络数据传输。
  3. 网络数据流具体处理细节交给CallServerInterceptor节点。
@Override public Response intercept(Chain chain) throws IOException {
    ... 
    StreamAllocation streamAllocation = realChain.streamAllocation();
    ...
    // 复用或创建新的RealConnection,并创建新的HttpCodec处理网络读写流。
    HttpCodec httpCodec = streamAllocation.newStream(client, doExtensiveHealthChecks);
    RealConnection connection = streamAllocation.connection();
    // 交给CallServerInterceptor处理网络流。
    return realChain.proceed(request, streamAllocation, httpCodec, connection);
  }

2.5 CallServerInterceptor

创建服务端的网络调用,向服务端发送请求并获取响应。

@Override public Response intercept(Chain chain) throws IOException {
    // 获取需要写请求和读响应的HttpCodec
    ... 
    long sentRequestMillis = System.currentTimeMillis();
    // 向服务端发送头部请求
    ...
    Response.Builder responseBuilder = null;
    // 如果含有支持的方法请求体,则需要向服务端发送请求体
    if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) {
      // 发送请求体
      ...
    }
    // 结束请求
    httpCodec.finishRequest();
    
    // 如果头部响应未读取,则读取头部响应
    if (responseBuilder == null) {
      responseBuilder = httpCodec.readResponseHeaders(false);
    }

    // 构建响应体
    ... 
    return response;
  }

2.6 自定义Interceptor

OkHttp3除了默认5种Interceptor实现,还可以添加Application层Network层的interceptor。

@OkHttpClient
    /*
    * 添加应用层Interceptor
    */
    public Builder addInterceptor(Interceptor interceptor) {
      interceptors.add(interceptor);
      return this;
    }
    
    /*
    * 添加网络层Interceptor
    */
    public List<Interceptor> networkInterceptors() {
      return networkInterceptors;
    }
  1. 应用层Interceptors

Application Interceptors对每个请求只调用一次,处理BridgeInterceptor返回的响应。可以不调用Chain.proceed()或多次调用Chain.proceed()。

  1. 网络层Interceptors

Network InterceptorsConnectInterceptorCallServerInterceptor之间调用。涉及到网络相关操作都会经过Network Interceptors,因此可以在缓存响应数据的之前对响应数据进行预处理。与Application Interceptors不同的是不支持短路处理,必须且只能调用一次Chain.proceed()方法,保证链式调用唯一。

一个简单的LogInterceptors

class LoggingInterceptor implements Interceptor {
  @Override public Response intercept(Interceptor.Chain chain) throws IOException {
    Request request = chain.request();

    long t1 = System.nanoTime();
    logger.info(String.format("Sending request %s on %s%n%s",
        request.url(), chain.connection(), request.headers()));

    Response response = chain.proceed(request);

    long t2 = System.nanoTime();
    logger.info(String.format("Received response for %s in %.1fms%n%s",
        response.request().url(), (t2 - t1) / 1e6d, response.headers()));

    return response;
  }
}

转载于:https://my.oschina.net/alexwan/blog/886712

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值