Android - 剖析OKHttp(4)- 拦截器RetryAndFollowUpInterceptor

·  阅读 35

源码分析基于 3.14.4

RetryAndFollowUpInterceptor作用

从名字就能看出,他是负责失败重试以及重定向。

//RetryAndFollowUpInterceptor类中
@Override public Response intercept(Chain chain) throws IOException {
 ......
  while (true) {
    ......
    try {
      response = realChain.proceed(request, transmitter, null);//1
      success = true;
    } catch (RouteException e) {//2
      if (!recover(e.getLastConnectException(), transmitter, false, request)) {
        throw e.getFirstConnectException();
      }
      continue;
    } catch (IOException e) {//3
      boolean requestSendStarted = !(e instanceof ConnectionShutdownException);
      if (!recover(e, transmitter, requestSendStarted, request)) throw e;
      continue;
    } 


    if (priorResponse != null) {
      response = response.newBuilder()
          .priorResponse(priorResponse.newBuilder()
                  .body(null)
                  .build())
          .build();
    }

    Exchange exchange = Internal.instance.exchange(response);
    Route route = exchange != null ? exchange.connection().route() : null;
    Request followUp = followUpRequest(response, route);//4
    ```
    if (followUp == null) {//6
        if (exchange != null && exchange.isDuplex()) {
           transmitter.timeoutEarlyExit();
        }
      return response;
    }

    ......
    RequestBody followUpBody = followUp.body();
    if (followUpBody != null && followUpBody.isOneShot()) {
      return response;
    }
    ......
    if (++followUpCount > MAX_FOLLOW_UPS) {///5
      throw new ProtocolException("Too many follow-up requests: " + followUpCount);
    }

    request = followUp;
    priorResponse = response;
  }
}
复制代码
  • 核心功能在intercept函数中;
  • 注释1:一般的拦截器都会有预处理,然后再丢给下一个拦截器处理,但是它没有,它直接丢给下一个拦截器了;
  • 注释2:如果发生RouteException(路由异常),通常是因为跟服务器没有建立连接,例如域名对应多个ip,其中一个ip连接失败,则重试另外的ip;
  • 注释3:如果发IOException,连接已经建立,在读写的过程中服务器发生了异常,例如down机了,则重试;;
  • 注释4:当不是发生 RouteException或IOException异常,则通过followUpRequest返回结果判断是否需要重试;
  • 注释5:限制重试次数为20次;
  • 注释6:当followUp为null,则表示返回结果正常,即返回结果;
//RetryAndFollowUpInterceptor类中
private Request followUpRequest(Response userResponse, @Nullable Route route) throws IOException {
    ......
    switch (responseCode) {
      case HTTP_PROXY_AUTH://1
        ......
        return client.proxyAuthenticator().authenticate(route, userResponse);
      case HTTP_UNAUTHORIZED://2
        return client.authenticator().authenticate(route, userResponse);
      ......
      case HTTP_MOVED_PERM://3
      case HTTP_MOVED_TEMP://4
        if (!client.followRedirects()) return null;
        String location = userResponse.header("Location");
        if (location == null) return null;
        HttpUrl url = userResponse.request().url().resolve(location);
        ......
        return requestBuilder.url(url).build();

      case HTTP_CLIENT_TIMEOUT://5
        ......
        return userResponse.request();
        ......
      default:
        return null;
    }
  }
复制代码
  • 主要是根据服务器返回码判断是否需要重试;
  • 注释1、2:当返回码为401或者407,表示鉴权有问题,会回调client.authenticator().authenticate(route, userResponse)或者client.proxyAuthenticator().authenticate(route, userResponse),提供一次获取新验证码写入Request头的机会,接着开始重试;
  • 注释3、4:当返回码为301或者302,表示需要重定向,则用新的url构建Request开始重试;301或者302的响应头,带有Location,就是重定向的地址,例如Location:www.baidu.com
  • 注释5:返回码408,如果允许重试(client.retryOnConnectionFailure()为true),并且服务器没有返回Retry-After 延迟时间,则可以立即开始重试;
  • 301表示永久重定向,302表示临时重定向,408表示请求超时,401表示授权失败,407表示代理授权失败;

总结:

  • RetryAndFollowUpInterceptor负责失败重试以及重定向;
  • RetryAndFollowUpInterceptor.intercept函数,内部是while循环处理,当正常返回结果则退出循环,否则重试;
  • 当发生RouteException或IOException或根据返回码,考虑需要重试;
  • 301表示永久重定向,302表示临时重定向,408表示请求超时,401表示授权失败,407表示代理授权失败;
  • 重试次数限制在20次;

以上分析有不对的地方,请指出,互相学习,谢谢哦!

分类:
Android
标签:
分类:
Android
标签:
收藏成功!
已添加到「」, 点击更改