okHttp框架分析--拦截链

概要

我把拦截器作为作为OkHttp框架的首篇是因为我认为它是OkHttp框架的精髓所在,无论我们的任何请求方式,最终都要经过一个个的拦截器,实现对网络的访问,它使用的是责任链模式,作用是在访问网络之前做一些 预前工作。

拦截链的组成

[ 1 ] 用户自定义拦截器​ - cient.interceptors()
[ 2 ] 重试并跟踪拦截器​ - RetryAndFollowUpInterceptor(client)
[ 3 ] 桥接拦截器​ - BridgeInterceptor(client.cookieJar())
[ 4 ] 缓存拦截器​ - CacheInterceptor(client.internalCache())
[ 5 ] 连接拦截器​ - ConnectInterceptor(client)
[ 6 ] 用户自定义网络拦截器​ - client.networkInterceptors()
[ 7 ] 服务器调用拦截器​ - CallServerInterceptor(forWebSocket)

其中 用户自定义拦截器用户自定义网络拦截器 是用户自定义的,可以根据处理的业务自定义拦截器的功能。

源码剖析

拦截器链流程.jpg

外部调用入口

public class GetExample {
   //声明一个OkHttpClient对象
  OkHttpClient client = new OkHttpClient();

  String run(String url) throws IOException {
    //声明Request请求
    Request request = new Request.Builder()
        .url(url)
        .build();
    // [ 1 ] 调用execute()方法,进行网络请求,以此方法为入口调取拦截链
    try (Response response = client.newCall(request).execute()) {
      return response.body().string();
    }
  }

  public static void main(String[] args) throws IOException {
    GetExample example = new GetExample();
    String response = example.run("https://raw.github.com/square/okhttp/master/README.md");
    System.out.println(response);
  }
}

[1] client.newCall(request)方法声明了一个RealCall对象,而方法execute()进入了RealCall类中execute()实现方法后,进而调取方法getResponseWithInterceptorChain()

fun getResponseWithInterceptorChain(): Response {
  //添加完整的 拦截器链
  val interceptors = ArrayList<Interceptor>()
  //添加 [用户自定义] 拦截器​
  interceptors.addAll(cient.interceptors())  
 //添加重试并跟踪拦截器​
  interceptors.add(RetryAndFollowUpInterceptor(client))
  //添加桥接拦截器​
  interceptors.add(BridgeInterceptor(client.cookieJar()))
 //添加缓存拦截器​
  interceptors.add(CacheInterceptor(client.internalCache()))
  //添加连接拦截器​
  interceptors.add(ConnectInterceptor(client))
  if (!forWebSocket) ​{
    //添加 [用户自定义] 网络拦截器​
    interceptors.addAll(client.networkInterceptors())
  }
  //添加最后一个服务器调用拦截器​
  interceptors.add(CallServerInterceptor(forWebSocket))
 //获取从拦截器链中 获取 第0个位置的拦截器包装成 RealInterceptorChain类型
  val chain = RealInterceptorChain(interceptors, transmitter, null, 0,
      originalRequest, this, client.connectTimeoutMillis(),
      client.readTimeoutMillis(), client.writeTimeoutMillis())
  var calledNoMoreExchanges = false
  try {
   //[ 2 ]​调取开始执行的方法
    val response = chain.proceed(originalRequest)
    if (transmitter.isCanceled) {
      closeQuietly(response)
      throw IOException("Canceled")
    }
    return response
  } catch (e: IOException) {
    calledNoMoreExchanges = true
    throw transmitter.noMoreExchanges(e) as Throwable
  } finally {
    if (!calledNoMoreExchanges) {
      transmitter.noMoreExchanges(null)
    }
  }
}

[ 2 ] 组织添加完所有的拦截器后,获取拦截链中第一个拦截器,调用chain.proceed(originalRequest)`方法,进行执行

fun proceed(request: Request, transmitter: Transmitter, exchange: Exchange?): Response {
  //判定当前 index是否 大于  ​拦截链的总数
  if (index >= interceptors.size) throw AssertionError()
  calls++  //调取次数
​  //如果我们已经有一个流,请确认传入的请求将使用它
  if (this.exchange != null && !this.exchange.connection().supportsUrl(request.url())) {
    throw IllegalStateException("network interceptor " + interceptors[index - 1] +
        " must retain the same host and port")
  }
  //如果我们已经有了一个流,请确认这是对chain.proceed()的唯一调用
  if (this.exchange != null && calls > 1) {
    throw IllegalStateException("network interceptor " + interceptors[index - 1] +
        " must call proceed() exactly once")
  }
  // 获取链中的下一个拦截器的实例,封装成 RealInterceptorChain
  val next = RealInterceptorChain(interceptors, transmitter, exchange,
      index + 1, request, call, connectTimeout, readTimeout, writeTimeout)
//获取当前的拦截器​
  val interceptor = interceptors[index] 
  @Suppress("USELESS_ELVIS")
  // [3] 执行当前拦截器的 intercept函数,并将下一个拦截器实例作为参数传入​
  val response = interceptor.intercept(next) ?: throw NullPointerException(
      "interceptor $interceptor returned null")
  //确认下一个拦截器调用了chain.proceed()
  if (exchange != null && index + 1 < interceptors.size && next.calls != 1) {
    throw IllegalStateException("network interceptor " + interceptor +
        " must call proceed() exactly once")
  }
  if (response.body() == null) {
    throw IllegalStateException(
        "interceptor $interceptor returned a response with no body")
  }
  //返回最终的Response实例​
  return response
}

[ 3 ] 执行当前拦截器的 intercept函数,并将下一个拦截器实例作为参数传入​方法interceptor.intercept(next)

提示

拦截链中的一个拦截器是 用户自定义的,若用户不进行第一,则第二拦截器将处于第一位置,即RetryAndFollowUpInterceptor拦截器,我们假设用户未进行自定义,则当前将进入RetryAndFollowUpInterceptor.kt,执行方法interceptor.intercept(next)

public final class RetryAndFollowUpInterceptor implements Interceptor {
  private static final int MAX_FOLLOW_UPS = 20;
  private final OkHttpClient client;
  public RetryAndFollowUpInterceptor(OkHttpClient client) {
    this.client = client;
  }
  @Override public Response intercept(Chain chain) throws IOException {
    //获取下一个拦截器中的 请求
    Request request = chain.request();
    RealInterceptorChain realChain = (RealInterceptorChain) chain;
    //获取下一个拦截器的发射器​
    Transmitter transmitter = realChain.transmitter();
    int followUpCount = 0;
    Response priorResponse = null;
    while (true) {
     //准备创建一个携带{@code request}的流。如果存在连接,这更喜欢使用现有的 。
      transmitter.prepareToConnect(request);
      if (transmitter.isCanceled()) {
        throw new IOException("Canceled");
      }
      Response response;
      boolean success = false;
      try {
        //下一个拦截器调取proceed函数,进行执行​ 
        //​@重要 每一个拦截在完成自己所需处理的业务后,都回调取 @方法[ realChain.proceed(request, transmitter, null);]
        //然后在外部会传入 拦截链中的下一个拦截器实例,从而实现责任链模式,完成每一个链既定​业务,最后返回数据会逐次 [向上返]
        response = realChain.proceed(request, transmitter, null);
        success = true;
      } catch (RouteException e) {
        // The attempt to connect via a route failed. The request will not have been sent.
        if (!recover(e.getLastConnectException(), transmitter, false, request)) {
          throw e.getFirstConnectException();
        }
        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, transmitter, requestSendStarted, request)) throw e;
        continue;
      } finally {
        // 抛出一个网络调用异常,释放所有资源.
        if (!success) {
          transmitter.exchangeDoneDueToException();
        }
      }
      //以下处理皆要等待所有的拦截器执行完毕,逐次[向上]返
      ...
      priorResponse = response;
    }
  }

[4] 下一个拦截器调取proceed函数,进行执行​;每个[拦截器]完成既定的业务都会调取下一个拦截器,最后调取CallServerInterceptor.kt的方法interceptor.intercept(next)

@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
  val realChain = chain as RealInterceptorChain
  val exchange = realChain.exchange()
  //获取请求实例​
  val request = realChain.request()    
 //获取请求体​
  val requestBody = request.body()
  ...
​  //@重点 做判定,若当前请求方法不是 GET或 HEAD 并且 请求体不为 Null 则判定成立
  if (HttpMethod.permitsRequestBody(request.method()) && requestBody != null) {
    //如果请求上有一个“Expect: 100-continue”报头,则等待一个“HTTP/1.1 100”报头 
  //在发送请求体之前继续“响应”。如果我们没有收到,返回
​  //在没有传输请求体的情况下,我们确实得到了(例如4xx响应)
​
  //在[预用知识说明]标签  ==>  { “Expect: 100-continue”的来龙去脉 }  中做详细说明​
    if ("100-continue".equals(request.header("Expect"), ignoreCase = true)) {
      exchange.flushRequest()
      responseHeadersStarted = true
      //开始调用响应头​
      exchange.responseHeadersStart()
       // 读取响应头​
      responseBuilder = exchange.readResponseHeaders(true)
    }
   ​
    if (responseBuilder == null) {
      if (requestBody.isDuplex()) {
        // 准备一个双重主体,以便应用程序稍后可以发送请求主体。
        exchange.flushRequest()
        //创建一个buffer​
        val bufferedRequestBody = exchange.createRequestBody(request, true).buffer()
         //请求体写入buff中​
        requestBody.writeTo(bufferedRequestBody)
      } else {
        // 如果满足“Expect: 100-continue”期望,则编写请求主体
        val bufferedRequestBody = exchange.createRequestBody(request, false).buffer()
        requestBody.writeTo(bufferedRequestBody)
        bufferedRequestBody.close()
      }
    } else {
      exchange.noRequestBody()
      if (!exchange.connection().isMultiplexed) {
        //如果没有满足“Expect: 100-continue”期望,请阻止HTTP/1连接
      ​ //避免重复使用。否则,我们仍然有义务将请求体传输到 
       //让连接保持一致的状态。
        exchange.noNewExchangesOnConnection()
      }
    }
  } else {
     //没有请求体​
    exchange.noRequestBody()
  }
​​
​
  if (requestBody == null || !requestBody.isDuplex()) {
    //将所有缓冲数据写入底层接收器(如果存在的话)。然后这个sink是递归不断刷新
   //​将数据尽可能地推送到最终目的地。通常, 目标是一个网络套接字或文件。​
    exchange.finishRequest()
  }
  if (!responseHeadersStarted) {
    //仅在接收响应头之前调用。 
  //​ 连接是隐式的,通常与最后一个[connectionAcquired]事件相关。 
  //​这可以被调用超过1次为一个[Call]。​
    exchange.responseHeadersStart()
  }
  if (responseBuilder == null) {
    //从HTTP传输解析响应头的字节。​
    responseBuilder = exchange.readResponseHeaders(false)!!
  }
  //一个HTTP响应。
 //​该类的实例不是不可变的:响应体是一次性的
 //​只使用一次,然后关闭的值。所有其他属性都是不可变的。​
  var response = responseBuilder
      .request(request)
      .handshake(exchange.connection().handshake())
      .sentRequestAtMillis(sentRequestMillis)
      .receivedResponseAtMillis(System.currentTimeMillis())
      .build()
  var code = response.code()  //获取响应 Code
  if (code == 100) {
    // 服务器发送了100-continue,即使我们没有请求。
   ​ //再读一遍实际的响应
    response = exchange.readResponseHeaders(false)!!
        .request(request)
        .handshake(exchange.connection().handshake())
        .sentRequestAtMillis(sentRequestMillis)
        .receivedResponseAtMillis(System.currentTimeMillis())
        .build()
    code = response.code()
  }
   //在接收响应标头后立即调用​
  exchange.responseHeadersEnd(response)
  response = if (forWebSocket && code == 101) {
    //连接叠积,但我们需要确保拦截器看到非空响应体。
    response.newBuilder()
        .body(Util.EMPTY_RESPONSE)
        .build()
  } else {
    response.newBuilder()
        .body(exchange.openResponseBody(response))
        .build()
  }
 //关闭连接​
  if ("close".equals(response.request().header("Connection"), ignoreCase = true) ||
      "close".equals(response.header("Connection"), ignoreCase = true)) {
    exchange.noNewExchangesOnConnection()
  }
 //抛出连接异常​
  if ((code == 204 || code == 205) && response.body()?.contentLength() ?: -1 > 0) {
    throw ProtocolException(
        "HTTP $code had non-zero Content-Length: ${response.body()?.contentLength()}")
  }
  //返回响应数据​
  return response
}

重要

每一个拦截在完成自己所需处理的业务后,都回调取方法realChain.proceed(request, transmitter, null);
然后在外部会传入 拦截链中的下一个拦截器实例,从而实现责任链模式,完成每一个链既定​业务,进入下一个链的调用,最后会在CallServerInterceptor.kt的方法interceptor.intercept(next)向服务器请求数据,最后返回数据会逐次 向上返,然后进行下半部分的业务处理,最后将获取的Response对象返回至前台

This ALL! Thanks EveryBody!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,542评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,596评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,021评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,682评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,792评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,985评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,107评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,845评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,299评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,612评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,747评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,441评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,072评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,828评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,069评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,545评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,658评论 2 350

推荐阅读更多精彩内容