在《 Okhttp之CacheInterceptor简单分析 》这篇博客中简单的分析了下缓存拦截器的工作原理,通过此博客我们知道在执行完CacheInterceptor之后会执行下一个浏览器——ConnectInterceptor,本篇就对此拦截器的工作做简单的梳理,Connect顾名思义该拦截器的主要作用是打开了与服务器的链接,正式开启了网络请求,还是从intercept方法说起:

public Response intercept(Chain chain) throws IOException {
     。。。。。
    //从拦截器链里得到StreamAllocation对象
    StreamAllocation streamAllocation = realChain.streamAllocation();
    。。。。

   HttpCodec httpCodec = streamAllocation.newStream(client, doExtensiveHealthChecks);

    //获取realConnetion
    RealConnection connection = streamAllocation.connection();
    //执行下一个拦截器
    return realChain.proceed(request, streamAllocation, httpCodec, connection);
  }

从代码上来看该拦截器的主要功能都交给了StreamAllocation处理,且这个类是从拦截器链对象(RealInterceptorChain对象)上获取的,通过《Okhttp源码简单解析(一) 》这篇博客我们知道RealInterceptorChain是在RealCall的getResponseWithInterceptorChain方法初始化的

Response getResponseWithInterceptorChain() throws IOException {
     //省略部分与本篇博客无关的代码
    //将拦截器集合交给RealInterceptorChain这个Chain对象来处理
    Interceptor.Chain chain = new RealInterceptorChain(
        interceptors, null, null, null, 0, originalRequest);
    return chain.proceed(originalRequest);
  }

RealInterceptorChain构造函数第二个参数就是我们需要的StreamAllocation对象,但是,纳尼?为什么上面创建 RealInterceptorChain对象的时候第二个参数传的null?



其实,根据《Okhttp源码简单解析(一) 》这篇博客我们知道拦截器链的工作原理,且Okhttp内置拦截器链上第一个拦截器就是RetryAndFollowUpInterceptor,事实上StreamAllocation 就是在这个拦截器里面初始化的!

 private StreamAllocation streamAllocation;
Response intercept(Chain chain) throws IOException {

    //初始化streamAllocation对象
    streamAllocation = new StreamAllocation(
        client.connectionPool(), createAddress(request.url()), callStackTrace);

        //执行procced方法,将streamAllocation对象传给拦截器链
         response = ((RealInterceptorChain) chain).proceed(request, streamAllocation, null, null);
}

可以看出在RetryAndFollowUpInterceptor这个拦截器会初始化一个StreamAllocation交给拦截器链(也就是说一次发起一次请求就会生成一个StreamAllocation对象,但是StreamAllocation对象公用一个连接池)。那么这个StreamAllocation到底是神马玩意?该构造器里面有两个重要的参数:

1.使用了Okhttp的连接池ConnectionPool

2.通过url创建了一个Address对象。

Okhttp连接池简单说明

本篇只是对连接池做最简单的说明,内部的实现原理暂时不细讲。在Okhttp内部的连接池实现类为ConnectionPool,该类持有一个ArrayDeque队列作为缓存池,该队列里的元素为RealConnection(通过这个名字应该不难猜出RealConnection是来干嘛的)。

该链接池在初始化OkhttpClient对象的时候由OkhttpClient的Builder类创建,并且ConnectionPool提供了put、get、evictAll等操作。但是Okhttp并没有直接对连接池进行获取,插入等操作;而是专门提供了一个叫Internal的抽象类来操作缓冲池:比如向缓冲池里面put一个RealConnection,从缓冲池get一个RealConnection对象,该类里面有一个public且为static的Internal类型的引用:

//抽象类
public abstract class Internal {
  public static Internal instance;
}

instance的初始化是在OkhttpClient的static语句块完成的:

static {
    Internal.instance = new Internal() {
       //省略部分代码
    };
  }

在文章开头ConnectionInterceptor的intercept方法中拿到StreamAllocation对象之后就调用了newStream方法,还是先说说这个方法是干什么的吧,然后在分析其源码(带着结论讲解源码能方便的说明问题),newStream方法主要做了工作:

1)从缓冲池ConnectionPool获取一个RealConnection对象,如果缓冲池里面没有就创建一个RealConnection对象并且放入缓冲池中,具体的说是放入ConnectionPool的ArrayDeque队列中。

2)获取RealConnection对象后并调用其connect**打开Socket链接**

下面就分析其源码:

public HttpCodec newStream(OkHttpClient client, boolean doExtensiveHealthChecks) {
   。。。
//获取一个RealConnection对象
RealConnection resultConnection = findHealthyConnection(。。。);

//获取HttpCodec 对象
 HttpCodec resultCodec = resultConnection.newCodec(client, this);

 //返回HttpCodec对象
      synchronized (connectionPool) {
        codec = resultCodec;
        return resultCodec;
      }
  }

仅从上面的代码来看该方法主要就是做了两件事:

1、调用findHealthyConnection获取一个RealConnection对象。

2、通过获取到的RealConnection来生成一个HttpCodec对象并返回之。

所以,看看findHealthyConnection做了神马?

 private RealConnection findHealthyConnection(。。。) {
    while (true) {//一个循环
     //获取RealConnection对象
      RealConnection candidate = findConnection(。。。);

      synchronized (connectionPool) {
        //直接返回之
        if (candidate.successCount == 0) {
          return candidate;
        }
      }

      //对链接池中不健康的链接做销毁处理
      if (!candidate.isHealthy(doExtensiveHealthChecks)) {
        noNewStreams();
        continue;
      }
      //返回
      return candidate;
    }//end while
  }

上面代码也很简单:

1、开启一个while循环,调用findConnection继续获取RealConnection对象candidate 。

2、如果candidate 的successCount 为0,直接返回之,while循环结束

3、如果candidate是一个不“健康”的对象,则对此对象进行调用noNewStreams进行销毁处理,继续循环调用findConnection获取RealConnection对象。

(注:不健康的RealConnection条件为如下几种情况:

RealConnection对象 socket没有关闭

socket的输入流没有关闭

socket的输出流没有关闭

http2时连接没有关闭

所以继续看看findConnection方法做了些神马?

private RealConnection findConnection(。。。){
     //选中的路由
    Route selectedRoute;
    synchronized (connectionPool) {
        。。。。
        //尝试复用
      RealConnection allocatedConnection =this.connection;
       //可以复用直接返回
      if (allocatedConnection != null && !allocatedConnection.noNewStreams) {
        return allocatedConnection;
      }
      //从连接池获取一个连接,此时
      Internal.instance.get(connectionPool, address, this, null);
      //成功从连接池中获取一个连接,返回之
      if (connection != null) {
        return connection;
      }
      //当前对象使用的路由对象
      selectedRoute = route;
    }

    // If we need a route, make one. This is a blocking operation.
    if (selectedRoute == null) {//阻塞操作
      selectedRoute = routeSelector.next();
    }

    RealConnection result;
    synchronized (connectionPool) {
      //从缓冲池中获取对象
      Internal.instance.get(connectionPool, address, this, selectedRoute);
        //缓存池中有此连接
      if (connection != null) return connection;

      //当前对象新的路由对象
      route = selectedRoute;
      refusedStreamCount = 0;
      //缓存池中没有此连接,初始化一个 ,
      result = new RealConnection(connectionPool, selectedRoute);
      //将当前StreamAllocation的弱引用
      //交给result的allocations集合里
      //并将result赋值给this.connection这个引用
      acquire(result);
    }

      //开始Socket连接,为阻塞操作
    result.connect(connectTimeout, readTimeout, writeTimeout, connectionRetryEnabled);
    routeDatabase().connected(result.route());

    Socket socket = null;
    synchronized (connectionPool) {
      // 将新创建的RealConection放入到缓冲池
      Internal.instance.put(connectionPool, result);

      // If another multiplexed connection to the same address was created concurrently, then
      // release this connection and acquire that one
      //如果另外的多路复用连接在同时床架,则释放此连接,用另外的链接
      if (result.isMultiplexed()) {//此处代码暂不分析
        socket = Internal.instance.deduplicate(connectionPool, address, this);
        result = connection;
      }
    }
    closeQuietly(socket);

    return result;
  }

结合代码中的注释,这个类大体上做了如下几个工作:

1、StreamAllocation的connection能复用就复用之

2、如果connection不能复用,则从连接池中获取RealConnection,获取成功则返回,从连接池中获取RealConnection的方法调用了两次

,第一次没有传Route,第二次传了(这个坑以后讲解)

3,如果连接池里没有则new一个RealConnection对象,并放入连接池中

4.最终调用RealConnection的connect方法打开一个socket链接(此处暂且说结论,至于为何断定是socket链接,篇幅有限另外结合别的知识点另开博文说明)。

到此为止,ConnectionInterceptor简单分析完毕,分析了这么多,总的来说ConnectionInterceptor就是弄一个RealConnection对象,然后创建Socket链接,并且调用下一个也是最后一个拦截器来完成Okhttp的整个操作。

限于篇幅原因,虽然还有好多知识点和要点没有讲解,但是为了本篇博客的主题不至于跑偏,本篇博客就此完结,关于里面涉及的另外的知识点和要点会另外写博客专门梳理,如有不当之处,欢迎批评指正

OkHttp之ConnectInterceptor简单分析的更多相关文章

  1. Okhttp之CallServerInterceptor简单分析

    在Okhttp源码分析专栏的几篇博客分析了Okhttp几个拦截器的主要功能,还剩下最后一个拦截器CallServerInterceptor没有分析,本篇博客就简单分析下该拦截器的功能. 在Okhttp ...

  2. Okhttp之CacheInterceptor简单分析

    <OkHttp之BridgeInterceptor简单分析 >简单分析了BridgeInterceptor的工作原理,在Okhttp的拦截器链上BridgeInterceptor的下一个拦 ...

  3. OkHttp之BridgeInterceptor简单分析

    在< Okhttp源码简单解析(一) >这篇博客简单分析了Okhttp请求的执行流程,通过该篇博客我们知道OkHttp的核心网络请求中内置"拦截器"发挥了重大作用:本篇 ...

  4. Okhttp之RealConnection建立链接简单分析

    在之前的博客中我们知道Okhttp在发起链接请求先从链接池中获取连接,如果链接池中没有链接则创建新的链接RealConnection对象,然后执行其connet方法打开SOCKET链接(详见< ...

  5. Okhttp之连接池ConnectionPool简单分析(一)

    开篇声明:由于本篇博文用到的一些观点或者结论在之前的博文中都已经分析过,所以本篇博文直接拿来用,建议读此博文的Monkey们按照下面的顺序读一下博主以下博文,以便于对此篇博文的理解: <Okht ...

  6. Okhttp对http2的支持简单分析

    在< Okhttp之RealConnection建立链接简单分析>一文中简单的分析了RealConnection的connect方法的作用:打开一个TCP链接或者打开一个隧道链接,在打开t ...

  7. Okhttp之RouteSelector简单解析

    继前面的几篇OKhttp的拦截器简单分析之后,对于后续Okhttp之间的分析自己也着实琢磨了一段时间,是分析RealConnection?还是ConnectionPool,随着对Okhttp源码的深入 ...

  8. 简单分析JavaScript中的面向对象

    初学JavaScript的时候有人会认为JavaScript不是一门面向对象的语言,因为JS是没有类的概念的,但是这并不代表JavaScript没有对象的存在,而且JavaScript也提供了其它的方 ...

  9. CSipSimple 简单分析

    简介 CSipSimple是一款可以在android手机上使用的支持sip的网络电话软件,可以在上面设置使用callda网络电话.连接使用方式最好是使用wifi,或者3g这样上网速度快,打起电话来效果 ...

随机推荐

  1. /usr/bin/ld: crti.o: No such file: No such file or directory

    Problem : You are running a 64-bit linux system and trying to compile a 32-bit application and you g ...

  2. HTTP-java访问https资源时,忽略证书信任问题,代码栗子

    java程序在访问https资源时,出现报错 sun.security.validator.ValidatorException: PKIX path building failed: sun.sec ...

  3. eclipse 工程没有build path

    项目的.project文件添加: <buildSpec><buildCommand><name>org.eclipse.jdt.core.javabuilder&l ...

  4. 【Network Architecture】Feature Pyramid Networks for Object Detection(FPN)论文解析(转)

    目录 0. 前言 1. 博客一 2.. 博客二 0. 前言   这篇论文提出了一种新的特征融合方式来解决多尺度问题, 感觉挺有创新性的, 如果需要与其他网络进行拼接,还是需要再回到原文看一下细节.这里 ...

  5. Mybatis中传参包There is no getter for property named 'XXX' in 'class java.lang.String'

    Mybatis中传参包There is no getter for property named 'XXX' in 'class java.lang.String' 一.发现问题 <select ...

  6. SVN 与Eclipse 关联 || 安装beyond 插件

    1.让本地svn代码与库建立联系   右击项目名称,Team -  share project 2.本地svn版本一般与Eaclipse svn插件 版本一致!http://subclipse.tig ...

  7. 为什么U盘在拔出之前需要“安全弹出”?

    前言 我们不知道从什么时候开始有一个观念:U盘一定要点击“安全弹出”才能拔.那么是不是在任何情况下都必须要这样呢? 介绍 U盘的传输策略有两种: 写入缓存:这种策略在windows中称为“更好的性能” ...

  8. mongo docker image

    mongo 保存压缩镜像 docker save -o ~/Desktop/mongo.tar mongo 7za a -mx=9 ~/Desktop/mongo.tar{.7z,} 导入或拉取镜像 ...

  9. Regression 手动实现Gradient Descent

    import numpy as np import matplotlib.pyplot as plt x_data = [338.,333.,328.,207.,226.,25.,179.,60.,2 ...

  10. mvc框架详解

    mvc全称:Model View Controller,分别为Model(模型),View(视图),Controller(控制器). 这张图就很好的解释了MVC框架的基本工作原理,Modal通常为后台 ...