时间: 2020-08-31|tag: 37次围观|0 条评论

RetryAndFollowUpInterceptor拦截器分析

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

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

整体结构

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

OKhttp源码学习(四)—— RetryAndFollowUpInterceptor插图
整体结构

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

  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)

文章转载于:https://www.jianshu.com/p/fc2c34354ea4

原著是一个有趣的人,若有侵权,请通知删除

本博客所有文章如无特别注明均为原创。
复制或转载请以超链接形式注明转自起风了,原文地址《OKhttp源码学习(四)—— RetryAndFollowUpInterceptor
   

还没有人抢沙发呢~