OKhttp源码学习(四)—— RetryAndFollowUpInterceptor

RetryAndFollowUpInterceptor拦截器分析

源码地址:https://github.com/square/okhttp

前面已经对整体流程以及几个类做了了解,这里就开始对第一个拦截器RetryAndFollowUpInterceptor的分析了。

整体结构

首先通过一张图了解一下这个拦截器的整体结构:

OKhttp源码学习(四)—— RetryAndFollowUpInterceptor_第1张图片
整体结构

纵观整个类,方法分为了两部分:

  1. 供外部调用的:cancle相关的 , intercept等 ;
  2. 内部使用的,主要服务于intercept方法。

那么接下来我们就从两部分对这个类进行分析:

外部调用方法


//进行取消连接的操作
  public void cancel() {
    canceled = true;
  //通过StreamAllocation进行cancle操作
    StreamAllocation streamAllocation = this.streamAllocation;
    if (streamAllocation != null) streamAllocation.cancel();
  }

//获取cancle状态
  public boolean isCanceled() {
    return canceled;
  }

//设置调用堆栈跟踪,在创建StreamAllocation对象的时候作为参数传入
  public void setCallStackTrace(Object callStackTrace) {
    this.callStackTrace = callStackTrace;
  }

//获取StreamAllocation,一个流分配的类,处理连接,数据流,Call请求的关系
  public StreamAllocation streamAllocation() {
    return streamAllocation;
  }

这些方法提供给外部调用,你跟踪一下,会发现其实都是给RealCall调用使用的,而Cancle相关的方法,RealCall 就直接提供给外部使用了。也就是我们取消一个连接的操作。而另外两个方法,则都是提供给内部使用。

这个几个方法中,你会发现,大部分都与这个StreamAllocation类有关。这个类到底是什么?到底做了什么操作?在后面还会用到吗?带着这些疑问,在这一节之后再对这个类进行学习分析。

intercept方法

每个拦截器,最重要的方法就是这个intercept方法了。
而对于RetryAndFollowUpInterceptor这个拦截器都做了些什么?

  1. 新建StreamAllocation类,传入okhttpClicent中创建的连接池,传入通过url创建Address类,传入callStackTrace,调用堆栈跟踪。

  2. 开启一个while循环;

  3. 判读是否用户已经cancle,是-抛出异常,否-继续走下去;

  4. 通过RealInterceptorChain调用下一个拦截器(request, streamAllocation),并等待下一个拦截器返回结果。

  5. 获取返回结果做两个捕获异常,处理后面步骤抛出的异常, 判断是否继续请求。

  6. 判读结果是否符合要求,返回或者进行重定向。

  7. 关闭响应结果

  8. 判断重定向数目是否超过 MAX_FOLLOW_UPS(20)

  9. 判断重新连接是否问相同的连接,否-新建,是-释放。

  10. 循环2以后的步骤。

源码:

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

   //1. 新建StreamAllocation类
    streamAllocation = new StreamAllocation(
        client.connectionPool(), createAddress(request.url()), callStackTrace);

    int followUpCount = 0;
    Response priorResponse = null;
   //2.开启一个while循环
    while (true) {
   //3.cancle判读
      if (canceled) {
        streamAllocation.release();
        throw new IOException("Canceled");
      }

      Response response = null;
      boolean releaseConnection = true;
      try {
       //4.调用下一个拦截器
        response = ((RealInterceptorChain) chain).proceed(request, streamAllocation, null, null);
        releaseConnection = false;
      } catch (RouteException e) {
        // The attempt to connect via a route failed. The request will not have been sent.
       //5.判断是否继续请求
        if (!recover(e.getLastConnectException(), false, request)) {
          throw e.getLastConnectException();
        }
        releaseConnection = false;
        continue;
      } catch (IOException e) {
        // An attempt to communicate with a server failed. The request may have been sent.
        boolean requestSendStarted = !(e instanceof ConnectionShutdownException);
        if (!recover(e, requestSendStarted, request)) throw e;
        releaseConnection = false;
        continue;
      } finally {
        // We're throwing an unchecked exception. Release any resources.
        if (releaseConnection) {
          streamAllocation.streamFailed(null);
          streamAllocation.release();
        }
      }

      // Attach the prior response if it exists. Such responses never have a body.
      if (priorResponse != null) {
        response = response.newBuilder()
            .priorResponse(priorResponse.newBuilder()
                    .body(null)
                    .build())
            .build();
      }

     //6.判断是否重定向或者超时重试
      Request followUp = followUpRequest(response);

      if (followUp == null) {
        if (!forWebSocket) {
          streamAllocation.release();
        }
        return response;
      }

      // 7. 关闭响应结果
      closeQuietly(response.body());

     // 8.判断是否重定向数目
      if (++followUpCount > MAX_FOLLOW_UPS) {
        streamAllocation.release();
        throw new ProtocolException("Too many follow-up requests: " + followUpCount);
      }

      if (followUp.body() instanceof UnrepeatableRequestBody) {
        streamAllocation.release();
        throw new HttpRetryException("Cannot retry streamed HTTP body", response.code());
      }

     //判断是否相同的连接
      if (!sameConnection(response, followUp.url())) {
        streamAllocation.release();
        streamAllocation = new StreamAllocation(
            client.connectionPool(), createAddress(followUp.url()), callStackTrace);
      } else if (streamAllocation.codec() != null) {
        throw new IllegalStateException("Closing the body of " + response
            + " didn't close its backing stream. Bad interceptor?");
      }

      request = followUp;
      priorResponse = response;
    }
  }

内部调用方法

提供内部的方法都是服务于interceptor(),有三个:

  1. createAddress,通过url的host,port, dns 以及一系列ohHttpClient的协议连接参数,简历一个Address类。而这个Address对象是提供给StreamAllocation,建立连接使用的。

  2. recover, 连接异常判断,是否继续。(含isRecoverable)

  private boolean recover(IOException e, boolean requestSendStarted, Request userRequest) {
    streamAllocation.streamFailed(e);

    //调用层面,是否禁止了重连重试
    // The application layer has forbidden retries.
    if (!client.retryOnConnectionFailure()) return false;

    //请求的Request本身出错,不能继续再连接
    // We can't send the request body again.
    if (requestSendStarted && userRequest.body() instanceof UnrepeatableRequestBody) return false;
   
    // 是否可以恢复的,主要判断了,是否协议异常,中断异常,SSL的握手异常,SSL通道未许可异常。
    // This exception is fatal.
    if (!isRecoverable(e, requestSendStarted)) return false;

    //没有更多的线路提供了
    // No more routes to attempt.
    if (!streamAllocation.hasMoreRoutes()) return false;

    // For failure recovery, use the same route selector with a new connection.
    return true;
  }
  1. followUpRequest, 通过结果的http code码,来判断是否可以重定向,可以正常重定向会返回对应的Request,不然就返回null。

总结:
RetryAndFollowUpInterceptor 的主要做了三件事:

  1. 初始化了连接的对象(StreamAllocation,但是比没有真正建立连接,只是初始化了对象)(前置拦截);

  2. 通过RealInterceptorChain,再调用下一个拦截器;

  3. 收到结果之后,做异常处理,判断是否重连或者重定向,或者返回结果。(后置拦截)

作为第一个拦截器,功能也是相对比较简单的。留下一个疑问就是StreamAllocation 这个类,这个类的学习在以后会专门提出来。

系列:
OKhttp源码学习(一)—— 基本请求流程
OKhttp源码学习(二)—— OkHttpClient
OKhttp源码学习(三)—— Request, RealCall
OKhttp源码学习(五)—— BridgeInterceptor
OKhttp源码学习(六)—— CacheInterceptor拦截器
OKhttp源码学习(七)—— ConnectInterceptor拦截器
OKhttp源码学习(八)——CallServerInterceptor拦截器
OKhttp源码学习(九)—— 任务管理(Dispatcher)

你可能感兴趣的:(OKhttp源码学习(四)—— RetryAndFollowUpInterceptor)