OkHttp - Interceptors(三)

作者: Joe_H | 来源:发表于2017-04-09 19:10 被阅读0次

    本文中源码基于OkHttp 3.6.0

    这篇文章主要分析 ConnectInterceptor ,它是 OkHttp 请求链上的倒数第二个节点,其主要任务就是创建与服务器的连接。

    - ConnectInterceptor

    先来看其 intercept 方法。

    public Response intercept(Chain chain) throws IOException {
      RealInterceptorChain realChain = (RealInterceptorChain) chain;
      Request request = realChain.request();
      StreamAllocation streamAllocation = realChain.streamAllocation();
    
      // We need the network to satisfy this request. Possibly for validating a conditional GET.
      boolean doExtensiveHealthChecks = !request.method().equals("GET");
      HttpCodec httpCodec = streamAllocation.newStream(client, doExtensiveHealthChecks);
      RealConnection connection = streamAllocation.connection();
    
      return realChain.proceed(request, streamAllocation, httpCodec, connection);
    }
    

    这里的代码比较少,创建了一个 HTTP 编解码器HttpCodec和一个连接对象RealConnection,并将它们加入到请求链中,其中HttpCodec主要用于在CallServerInterceptor中向服务器发起 Request 和解析 Response。构建这两个对象的主要逻辑全部封装在StreamAllocation中,它在请求链的第一个节点RetryAndFollowupInterceptor中创建的。

    来看其 newStream() 方法。

    private RealConnection findHealthyConnection(int connectTimeout, int readTimeout,
        int writeTimeout, boolean connectionRetryEnabled, boolean doExtensiveHealthChecks)
        throws IOException {
      while (true) {
        RealConnection candidate = findConnection(connectTimeout, readTimeout, writeTimeout,
            connectionRetryEnabled);
    
        // 如果 connection 是一个新创建的,那么不用进行健康性测试
        synchronized (connectionPool) {
          if (candidate.successCount == 0) {
            return candidate;
          }
        }
    
        // 健康检查主要是判断 socket 是否关闭,输入输出流是否关闭等。
        // 如果连接不健康,则断开连接,并从 connectionPool 中移除。
        if (!candidate.isHealthy(doExtensiveHealthChecks)) {
          noNewStreams();
          continue;
        }
    
        return candidate;
      }
    }
    

    通过 findHealthyConnection() 找到一条可用的连接。

    private RealConnection findHealthyConnection(int connectTimeout, int readTimeout,
        int writeTimeout, boolean connectionRetryEnabled, boolean doExtensiveHealthChecks)
        throws IOException {
      while (true) {
        RealConnection candidate = findConnection(connectTimeout, readTimeout, writeTimeout,
            connectionRetryEnabled);
    
        // 如果 connection 是一个新创建的,那么不用进行健康性测试
        synchronized (connectionPool) {
          if (candidate.successCount == 0) {
            return candidate;
          }
        }
    
        // 健康检查主要是判断 socket 是否关闭,输入输出流是否关闭等。
        // 如果连接不健康,则断开连接,并从 connectionPool 中移除。
        if (!candidate.isHealthy(doExtensiveHealthChecks)) {
          noNewStreams();
          continue;
        }
    
        return candidate;
      }
    }
    

    继续进入 findConnection()。

    private RealConnection findConnection(int connectTimeout, int readTimeout, int writeTimeout,
        boolean connectionRetryEnabled) throws IOException {
      Route selectedRoute;
      synchronized (connectionPool) {
        if (released) throw new IllegalStateException("released");
        if (codec != null) throw new IllegalStateException("codec != null");
        if (canceled) throw new IOException("Canceled");
    
        // StreamAllocation 会保持了一个当前的连接,如果这个连接允许继续使用,则直接复用这个连接
        RealConnection allocatedConnection = this.connection;
        if (allocatedConnection != null && !allocatedConnection.noNewStreams) {
          return allocatedConnection;
        }
    
        // 从连接池中找到一个可以复用的连接
        Internal.instance.get(connectionPool, address, this);
        if (connection != null) {
          return connection;
        }
    
        selectedRoute = route;
      }
    
      // 如果没有合适的连接,那么就创建一个新的
      // 寻找一个合适的路由,如果没有找到就抛出异常,由 RetryAndFollowUpInterceptor 负责捕获并重试
      if (selectedRoute == null) {
        selectedRoute = routeSelector.next();
      }
    
      RealConnection result;
      synchronized (connectionPool) {
        route = selectedRoute;
        refusedStreamCount = 0;
        result = new RealConnection(connectionPool, selectedRoute);
        acquire(result);
        if (canceled) throw new IOException("Canceled");
      }
    
      // 执行三次握手,创建与服务器的连接
      result.connect(connectTimeout, readTimeout, writeTimeout, connectionRetryEnabled);
      routeDatabase().connected(result.route());
    
      Socket socket = null;
      synchronized (connectionPool) {
        // 把这个连接放进连接池,供以后复用
        Internal.instance.put(connectionPool, result);
    
        if (result.isMultiplexed()) {
          socket = Internal.instance.deduplicate(connectionPool, address, this);
          result = connection;
        }
      }
      closeQuietly(socket);
    
      return result;
    }
    

    在执行 connect() 的时候,使用 Socket 建立连接,如果请求是 HTTPS,则使用 SSLSocket 进行包装。

    那么,至此客户端与服务器之间的连接就已经建立成功了,如果 OkHttpClient 中设置了 networkInterceptor 的话,下面就该把任务交给它们处理了,这里就可以看出 networkInterceptor 和 Interceptor 之间的区别,networkInterceptor 处理请求时已经建立好连接。如果没有设置 networkInterceptor,下面就该 CallServerInterceptor 出场了。

    相关文章

      网友评论

        本文标题:OkHttp - Interceptors(三)

        本文链接:https://www.haomeiwen.com/subject/qrzdattx.html