OkHttp(一) 拦截器之开篇 内部拦截器简介以及 retryAndFollowUpInterceptor BridgeInterceptor CacheInterceptor

接上次的getResponseWithInterceptorChain()说
开局又是一张图 。。这是OkHttp内部提供的拦截器,实现网络监听、请求以及响应重写、请求失败重试等功能。
okhttpInterceptor
上面图里就是okhttp内部给我们提供的拦截器(责任链模式调用下面有介绍),当我们发起一个网络请求的时候Okhttp就会根据这个拦截器链来执行网络操作 上面说了是接getResponseWithInterceptorChain(同步异步最终都会执行的方法)来说 我们就看下这个里面到底执行了什么
getResponseWithInterceptorChain

//这个名字起的是真6 通过拦截器连获取响应(response)
Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    List<Interceptor> interceptors = new ArrayList<>();
    //添加用户自定义拦截器
    interceptors.addAll(client.interceptors());
    //重试和重定向拦截器
    interceptors.add(retryAndFollowUpInterceptor);
    //桥接适配拦截器 干嘛的。。。补充用户创建的请求中缺少的必要信息 就是补充请求头的
    interceptors.add(new BridgeInterceptor(client.cookieJar()));
    //缓存拦截器
    interceptors.add(new CacheInterceptor(client.internalCache()));
    //连接拦截器
    interceptors.add(new ConnectInterceptor(client));
    if (!forWebSocket) {
      interceptors.addAll(client.networkInterceptors());
    }
    interceptors.add(new CallServerInterceptor(forWebSocket));

    Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
        originalRequest, this, eventListener, client.connectTimeoutMillis(),
        client.readTimeoutMillis(), client.writeTimeoutMillis());

    return chain.proceed(originalRequest);
  }

ok 可以看到上面

  1. 是创建了一个拦截器的集合包括用户添加的拦截器和默认提供拦截器 也就是上面图中那5个拦截器
  2. 新建拦截器链new RealInterceptorChain()此时传递的index 是0 获取第一个拦截器
  3. 拦截器链 chain 调用proceed方法 chain.proceed();
  • 看一下proceed方法里面做了什么操作 下图主要代码
    proceed方法
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
      RealConnection connection) throws IOException {
      ···
    // Call the next interceptor in the chain.
    //调用链中的下一个拦截器  index +1 。
    RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
        connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
        writeTimeout);
    Interceptor interceptor = interceptors.get(index);
    Response response = interceptor.intercept(next);
    ···
    return response;
  }

可以看到方法内部
1、创建了一个index+1 的 RealInterceptorChain,这时创建的是下一个拦截器链next,
2、并调用当前的拦截器的intercept(next)方法将下一个拦截器(index+1)的拦截器传递进去
得到Response对象

  • 下面我们具体分析intercept方法是接口Interceptor的方法 所以我们看其其实现类这里我们分析重定向拦截器

intercept()

public Response intercept(Chain chain) throws IOException {
    Request request = chain.request();
    ···
    //1 创建StreamAllocation对象
	//分配流 用于网络连接操作 
	//获取连接服务端的connection和用于与服务端进行数据传输的输入输出流 。
    StreamAllocation streamAllocation = new StreamAllocation(client.connectionPool(),
        createAddress(request.url()), call, eventListener, callStackTrace);
  ···
    Response priorResponse = null;
    while (true) {
      //判断是否取消 取消抛异常
      if (canceled) {
        streamAllocation.release(true);
        throw new IOException("Canceled");
      }
···
      Response response;
      boolean releaseConnection = true;
      try {
      //2 调用proceed方法 方法内又创建一个RealInterceptorChain(index+1) 调用 intercep方法
      //执行下一个拦截器链
        response = realChain.proceed(request, streamAllocation, null, null);
        releaseConnection = false;
      } 
   
      Request followUp;
      try {
      //3 根据返回码判断是否重定向
        followUp = followUpRequest(response, streamAllocation.route());
      }
        // 4 判断是否重试 MAX_FOLLOW_UPS=20
    if (++followUpCount > MAX_FOLLOW_UPS) {
        streamAllocation.release(true);
        throw new ProtocolException("Too many follow-up requests: " + followUpCount);
      }
      request = followUp;
      priorResponse = response;
    }
  }

总结 intercept (next)方法

  • 1 调用传递进来的拦截器链next 的proceed() 方法
  • 2 返回response 对象

责任链

  • 1 RealInterceptorChain拦截器链调用proceed()方法
  • 2 proceed()方法里创建了一个index+1 的 RealInterceptorChain,这时创建的是下一个拦截器链next,
  • 3 通过index获取到当前执行到的拦截器,调用拦截器的intercept()方法,传递拦截器链next
  • 4 传递进来的拦截器链next又调用了next的proceed()方法,此时又从第一步开始一直到执行完所有的拦截器
  • 这样形成的链式调用也就是责任链 很像递归

每个拦截器的作用

1.RetryAndFollowUpInterceptor 网络请求重试和重定向拦截器
主要进行网络请求重试和重定向 然而并不是所有的网络请求都重试,它是有一定的限制范围的,OkHttp内部会帮我们检测网络异常和响应码的判断,如果都在它的限制范围内的话,才进行网络重连。

public Response intercept(Chain chain) throws IOException {
    Request request = chain.request();
    ···
    //1 创建StreamAllocation对象
	//分配流 用于网络连接操作 
	//获取连接服务端的connection和用于与服务端进行数据传输的输入输出流 。
    StreamAllocation streamAllocation = new StreamAllocation(client.connectionPool(),
        createAddress(request.url()), call, eventListener, callStackTrace);
  ···
    Response priorResponse = null;
    while (true) {
      //判断是否取消 取消抛异常
      if (canceled) {
        streamAllocation.release(true);
        throw new IOException("Canceled");
      }
···
      Response response;
      boolean releaseConnection = true;
      try {
      //2 调用proceed方法 方法内又创建一个RealInterceptorChain(index+1) 调用 intercep方法
      //执行下一个拦截器链
        response = realChain.proceed(request, streamAllocation, null, null);
        releaseConnection = false;
      } 
   
      Request followUp;
      try {
      //3 根据返回码判断是否重定向
        followUp = followUpRequest(response, streamAllocation.route());
      }
        // 4 判断是否重试 MAX_FOLLOW_UPS=20
    if (++followUpCount > MAX_FOLLOW_UPS) {
        streamAllocation.release(true);
        throw new ProtocolException("Too many follow-up requests: " + followUpCount);
      }
      request = followUp;
      priorResponse = response;
    }
  }

总结 RetryAndFollowUpInterceptor做了什么

  • 1 创建StreamAllocation对象,获取连接服务端的connection和用于与服务端进行数据传输的输入输出流 。这个对象在ConnectInterceptor中才用的到
  • 2 调用proceed方法 执行下一个拦截器,得到response 并返回给上一个拦截器
  • 3 根据返回状态值判断是否重试 若超过重试次数则抛异常

proceed 方法内创建了index+1的拦截器就是BridgeInterceptor

BridgeInterceptor 桥接适配拦截器
对request请求进行必要参数补充

@Override public Response intercept(Chain chain) throws IOException {
    Request userRequest = chain.request();
    Request.Builder requestBuilder = userRequest.newBuilder();

    RequestBody body = userRequest.body();
    // 配置request请求
    if (body != null) {
      MediaType contentType = body.contentType();
      if (contentType != null) {
        requestBuilder.header("Content-Type", contentType.toString());
      }

      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");
      }
    }

    if (userRequest.header("Host") == null) {
      requestBuilder.header("Host", hostHeader(userRequest.url(), false));
    }

    if (userRequest.header("Connection") == null) {
      requestBuilder.header("Connection", "Keep-Alive");
    }

    // If we add an "Accept-Encoding: gzip" header field we're responsible for also decompressing
    // the transfer stream.
    boolean transparentGzip = false;
    if (userRequest.header("Accept-Encoding") == null && userRequest.header("Range") == null) {
      transparentGzip = true;
      requestBuilder.header("Accept-Encoding", "gzip");
    }

    List<Cookie> cookies = cookieJar.loadForRequest(userRequest.url());
    if (!cookies.isEmpty()) {
      requestBuilder.header("Cookie", cookieHeader(cookies));
    }

    if (userRequest.header("User-Agent") == null) {
      requestBuilder.header("User-Agent", Version.userAgent());
    }
	//调用chain.proceed 执行下一个拦截器
    Response networkResponse = chain.proceed(requestBuilder.build());

    HttpHeaders.receiveHeaders(cookieJar, userRequest.url(), networkResponse.headers());

    Response.Builder responseBuilder = networkResponse.newBuilder()
        .request(userRequest);

    if (transparentGzip
        && "gzip".equalsIgnoreCase(networkResponse.header("Content-Encoding"))
        && HttpHeaders.hasBody(networkResponse)) {
      GzipSource responseBody = new GzipSource(networkResponse.body().source());
      Headers strippedHeaders = networkResponse.headers().newBuilder()
          .removeAll("Content-Encoding")
          .removeAll("Content-Length")
          .build();
      responseBuilder.headers(strippedHeaders);
      String contentType = networkResponse.header("Content-Type");
      responseBuilder.body(new RealResponseBody(contentType, -1L, Okio.buffer(responseBody)));
    }

    return responseBuilder.build();
  }

总结 BridgeInterceptor做了什么

  • 1 为发起的Request请求添加请求头部信息(类型、长度、host、 编码方式等),将用户构建的request 构建成可以发送网络请求的request
  • 2 调用proceed()方法执行下个拦截器, 获取 response
  • 3 将获得response处理(转换成用户可用的Response)包括 gzip 解压缩并返回给上一个拦截器(这里通过判断是否支持gzip压缩且请求头里面的"Content-Encoding"的value是否是"gzip"来判断是否需要进行gzip解压。)

proceed 方法内创建了index+1的拦截器变成了CacheInterceptor
CacheInterceptor 缓存拦截器 请参看该连接内容CacheInterceptor 缓存拦截器

感谢这个文章
后续的是ConnectInterceptor和CallServerInterceptor看下面连接的内容
OkHttp(三)拦截器之ConnectionInterceptor 与CallServerInterceptor

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值