OkHttp源碼解讀總結(十一)--->ConnectInterceptor攔截器

OkHttp源碼解讀總結(十一)—>ConnectInterceptor攔截器

標籤(空格分隔): OkHttp源碼 學習筆記


前言

  • 以下的相關知識總結是通過慕課網的相關學習和自己的相關看法,如果有需要的可以去查看一下慕課網的相關教學,感覺還可以。

連接池的概念

1、ConnectInterceptor攔截

打開與服務器之間的連接,正式開啓okhttp的網絡請求

@Override public Response intercept(Chain chain) throws IOException {
    //
    RealInterceptorChain realChain = (RealInterceptorChain) chain;
    Request request = realChain.request();
    //StreamAllocation創建  用來建立http請求所需要的所有網絡組件  在RetryAndFollowUpInterceptor重試連接器中 只是初始化了 但是沒有使用  他只是把創建好的StreamAllocation進行了向下傳遞   最終由ConnectInterceptor攔截器獲取和使用
    StreamAllocation streamAllocation = realChain.streamAllocation();

    // We need the network to satisfy this request. Possibly for validating a conditional GET.
    boolean doExtensiveHealthChecks = !request.method().equals("GET");
    //通過streamAllocation實例創建HttpCodec對象  用於編碼request和解碼response這個
    HttpCodec httpCodec = streamAllocation.newStream(client, chain, doExtensiveHealthChecks);
    //通過streamAllocation的connection()方法獲取RealConnection對象   用於進行實際的網絡io傳輸的 
    RealConnection connection = streamAllocation.connection();
    //調用proceed()方法  設置好了的攔截器
    return realChain.proceed(request, streamAllocation, httpCodec, connection);
  }

總結

  • 1、ConnectInterceptor獲取Interceptor傳過來的StreamAllocation對象,通過StreamAllocation.newStream()這個對象獲取HttpCodec對象,這個HttpCodec對象用於編碼request和解碼response
  • 2、將剛纔創建好的用於網絡IO的RealConnection對象,以及對於與服務器交互最爲關鍵的HttpCodec等對象傳遞給後面的攔截器

StreamAllocation.newStream()方法

 public HttpCodec newStream(
      OkHttpClient client, Interceptor.Chain chain, boolean doExtensiveHealthChecks) {
    int connectTimeout = chain.connectTimeoutMillis();
    int readTimeout = chain.readTimeoutMillis();
    int writeTimeout = chain.writeTimeoutMillis();
    boolean connectionRetryEnabled = client.retryOnConnectionFailure();

    try {
    //創建RealConnection對象   進行實際的網絡連接
      RealConnection resultConnection = findHealthyConnection(connectTimeout, readTimeout,
          writeTimeout, connectionRetryEnabled, doExtensiveHealthChecks);
      //創建HttpCodec對象
      HttpCodec resultCodec = resultConnection.newCodec(client, chain, this);
     //同步代碼塊  返回HttpCodec這個對象
      synchronized (connectionPool) {
        codec = resultCodec;
        return resultCodec;
      }
    } catch (IOException e) {
      throw new RouteException(e);
    }
  }

findHealthyConnection(connectTimeout, readTimeout,
writeTimeout, connectionRetryEnabled, doExtensiveHealthChecks)方法

private RealConnection findHealthyConnection(int connectTimeout, int readTimeout,
      int writeTimeout, boolean connectionRetryEnabled, boolean doExtensiveHealthChecks)
      throws IOException {
    while (true) {
      //獲取RealConnection
      RealConnection candidate = findConnection(connectTimeout, readTimeout, writeTimeout,
          connectionRetryEnabled);

      // If this is a brand new connection, we can skip the extensive health checks.
      synchronized (connectionPool) {
      //如果這個數量爲0  循環結束  
        if (candidate.successCount == 0) {
          return candidate;
        }
      }

      // Do a (potentially slow) check to confirm that the pooled connection is still good. If it
      // isn't, take it out of the pool and start again.
      if (!candidate.isHealthy(doExtensiveHealthChecks)) {
       //進行銷燬資源
        noNewStreams();
        //循環
        continue;
      }

      return candidate;
    }
  }

findConnection(int connectTimeout, int readTimeout, int writeTimeout,
boolean connectionRetryEnabled)方法

  private RealConnection findConnection(int connectTimeout, int readTimeout, int writeTimeout,
      boolean connectionRetryEnabled) throws IOException {
    boolean foundPooledConnection = false;
    RealConnection result = null;
    Route selectedRoute = null;
    Connection releasedConnection;
    Socket toClose;
    synchronized (connectionPool) {
      if (released) throw new IllegalStateException("released");
      if (codec != null) throw new IllegalStateException("codec != null");
      if (canceled) throw new IOException("Canceled");

      // Attempt to use an already-allocated connection. We need to be careful here because our
      // already-allocated connection may have been restricted from creating new streams.
      //複用
      releasedConnection = this.connection;
      toClose = releaseIfNoNewStreams();
      //不爲空
      if (this.connection != null) {
        // We had an already-allocated connection and it's good.
        //把這個result進行返回   如果可以複用就直接返回了
        result = this.connection;
        releasedConnection = null;
      }
      if (!reportedAcquired) {
        // If the connection was never reported acquired, don't report it as released!
        releasedConnection = null;
      }
     //如果不能複用
      if (result == null) {
      //從連接池當中獲取一個連接
        // Attempt to get a connection from the pool.
        Internal.instance.get(connectionPool, address, this, null);
        if (connection != null) {
          foundPooledConnection = true;
          result = connection;
        } else {
          selectedRoute = route;
        }
      }
    }
    closeQuietly(toClose);

    if (releasedConnection != null) {
      eventListener.connectionReleased(call, releasedConnection);
    }
    if (foundPooledConnection) {
      eventListener.connectionAcquired(call, result);
    }
    if (result != null) {
      // If we found an already-allocated or pooled connection, we're done.
      return result;
    }

    // If we need a route selection, make one. This is a blocking operation.
    boolean newRouteSelection = false;
    if (selectedRoute == null && (routeSelection == null || !routeSelection.hasNext())) {
      newRouteSelection = true;
      routeSelection = routeSelector.next();
    }

    synchronized (connectionPool) {
      if (canceled) throw new IOException("Canceled");

      if (newRouteSelection) {
        // Now that we have a set of IP addresses, make another attempt at getting a connection from
        // the pool. This could match due to connection coalescing.
        List<Route> routes = routeSelection.getAll();
        for (int i = 0, size = routes.size(); i < size; i++) {
          Route route = routes.get(i);
          Internal.instance.get(connectionPool, address, this, route);
          if (connection != null) {
            foundPooledConnection = true;
            result = connection;
            this.route = route;
            break;
          }
        }
      }

      if (!foundPooledConnection) {
        if (selectedRoute == null) {
          selectedRoute = routeSelection.next();
        }

        // Create a connection and assign it to this allocation immediately. This makes it possible
        // for an asynchronous cancel() to interrupt the handshake we're about to do.
        route = selectedRoute;
        refusedStreamCount = 0;
        result = new RealConnection(connectionPool, selectedRoute);
        acquire(result, false);
      }
    }

    // If we found a pooled connection on the 2nd time around, we're done.
    if (foundPooledConnection) {
      eventListener.connectionAcquired(call, result);
      return result;
    }

    // Do TCP + TLS handshakes. This is a blocking operation.
    //當獲取到這個鏈接之後 就進行實際的網絡連接
    result.connect(
        connectTimeout, readTimeout, writeTimeout, connectionRetryEnabled, call, eventListener);
    routeDatabase().connected(result.route());
   //
    Socket socket = null;
    synchronized (connectionPool) {
      reportedAcquired = true;

      // Pool the connection.
      //把result放入連接池中
      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
        socket = Internal.instance.deduplicate(connectionPool, address, this);
        //當前鏈接
        result = connection;
      }
    }
    //關閉socket
    closeQuietly(socket);

    eventListener.connectionAcquired(call, result);
    return result;
  }


嘗試獲取connection  如果可以複用 那麼就直接複用  如果不能複用 就從連接池中獲取一個connection  
  • 1、創建一個RealConnection對象
  • 2、選擇不同的鏈接方式
    • socket鏈接
    • 隧道鏈接
  • 3、CallServerInterceptor這個攔截器完成整個的okhttp的網絡請求
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章