了解源码系列----Okhttp

一、使用流程

1.同步

 OkHttpClient okHttpClient = new OkHttpClient();
        Request request = new Request.Builder().url("请求地址").build();
        try {
          Response response =   okHttpClient.newCall(request).execute();
            if (response.isSuccessful()) {
                Log.i("结果",response.body().string());
            }


        } catch (IOException e) {
            e.printStackTrace();
        }

2.异步

OkHttpClient okHttpClient = new OkHttpClient();
        Request request = new Request.Builder().url("请求地址").build();
        okHttpClient.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(@NotNull Call call, @NotNull IOException e) {

            }

            @Override
            public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {

                if (response.isSuccessful()) {
                    Log.i("结果", response.body().string());
                }
            }
        });

二、源码解析

1.初始化OkhttpClient

 public Builder() {
            dispatcher = new Dispatcher();//同步异步请求调度器,将请求加入Deque队列
            protocols = DEFAULT_PROTOCOLS;//http 协议  http1.2 http1.1
            connectionSpecs = DEFAULT_CONNECTION_SPECS;//连接配置  TLS连接 未加密是http
            eventListenerFactory = EventListener.factory(EventListener.NONE);
            proxySelector = ProxySelector.getDefault();//代理选择器
            cookieJar = CookieJar.NO_COOKIES;//Cookie配置
            socketFactory = SocketFactory.getDefault();
            hostnameVerifier = OkHostnameVerifier.INSTANCE;
            certificatePinner = CertificatePinner.DEFAULT;
            proxyAuthenticator = Authenticator.NONE;
            authenticator = Authenticator.NONE;//安全连接配置
            connectionPool = new ConnectionPool();//请求连接池
            dns = Dns.SYSTEM;
            followSslRedirects = true;
            followRedirects = true;
            retryOnConnectionFailure = true;
            connectTimeout = 10_000;
            readTimeout = 10_000;
            writeTimeout = 10_000;
            pingInterval = 0;
        }

2.Request

public final class Request {
  final HttpUrl url;//请求地址
  final String method;//请求方法
  final Headers headers;//相关头信息
  final @Nullable RequestBody body;//请求体
  final Object tag;

  private volatile CacheControl cacheControl; //头信息的 缓存 Lazily initialized.

  Request(Builder builder) {
    this.url = builder.url;
    this.method = builder.method;
    this.headers = builder.headers.build();
    this.body = builder.body;
    this.tag = builder.tag != null ? builder.tag : this;
  }

3.RealCall

通过OkhttpClient.newCall(request) 得到一个RealCall,首先来看execute请求
execute

 @Override
  public Response execute() throws IOException {
    synchronized (this) {
      //判断是否执行中 再次调用抛出异常
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    //重定向堆栈跟踪
    captureCallStackTrace();
    //请求添加监听
    eventListener.callStart(this);
    try {
      //添加到Dispatcher准备队列
      client.dispatcher().executed(this);
      //执行拦截器
      Response result = getResponseWithInterceptorChain();
      if (result == null) throw new IOException("Canceled");
      return result;
    } catch (IOException e) {
      eventListener.callFailed(this, e);
      throw e;
    } finally {
      //移除请求
      client.dispatcher().finished(this);
    }
  }

enqueue

public void enqueue(Callback responseCallback) {
    synchronized (this) {//TODO 判断是否执行中 再次调用抛出异常
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    //TODO 重定向堆栈跟踪
    captureCallStackTrace();
    eventListener.callStart(this);
    //TODO 交给 dispatcher调度器 进行调度  这里使用的是AsyncCall
    client.dispatcher().enqueue(new AsyncCall(responseCallback));
  }

4.Dispatcher 调度器
Dispatcher主要是保存同步和异步的队列,并执行异步AsyncCall
同步:使用一个Deque readyAsyncCalls 同步队列
异步:使用两个队列。一个是准备执行的队列readyAsyncCalls 一个是正在执行的队列runningAsyncCalls队列

同步
     /**
     * 同步请求 只是将Call添加到同步队列里
     * Used by {@code Call#execute} to signal it is in-flight.
     */
    synchronized void executed(RealCall call) {
        runningSyncCalls.add(call);
    }
 异步
 synchronized void enqueue(AsyncCall call) {
        // 同时请求不能超过并发数(64,可配置调度器调整)
        // okhttp会使用共享主机即 地址相同的会共享socket
        // 同一个host最多允许5条线程通知执行请求
        if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) <
                maxRequestsPerHost) {
            // 加入运行队列 并交给线程池执行
            // AsyncCall 是一个runnable,查看其execute实现
            runningAsyncCalls.add(call);
            //执行异步请求
            executorService().execute(call);
        } else {
            //加入等候队列
            readyAsyncCalls.add(call);
        }
    }

finished
同步和异步最后一个参数不同,异步需要执行promoteCalls,继续执行未完成的请求

 /**
     * Used by {@code AsyncCall#run} to signal completion.
     */
    void finished(AsyncCall call) {
        finished(runningAsyncCalls, call, true);
    }

    /**
     * Used by {@code Call#execute} to signal completion.
     */
    void finished(RealCall call) {
        finished(runningSyncCalls, call, false);
    }
 private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {
        int runningCallsCount;
        Runnable idleCallback;
        synchronized (this) {
            // 移除队列
            if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
            //检查执行 readyAsyncCalls 中的请求
            if (promoteCalls) promoteCalls();
            runningCallsCount = runningCallsCount();
            idleCallback = this.idleCallback;
        }
        //闲置调用
        if (runningCallsCount == 0 && idleCallback != null) {
            idleCallback.run();
        }
    }

异步执行
继续执行readyAsyncCall的请求

private void promoteCalls() {
        // 检查 运行队列 与 等待队列
        if (runningAsyncCalls.size() >= maxRequests) return; // Already running max capacity.
        if (readyAsyncCalls.isEmpty()) return; // No ready calls to promote.

        for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
            AsyncCall call = i.next();
            //  相同host的请求没有达到最大
            if (runningCallsForHost(call) < maxRequestsPerHost) {
                i.remove();
                runningAsyncCalls.add(call);
                executorService().execute(call);
            }

            if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity.
        }
    }

AsyncCall
看看异步请求的线程,很简单
在这里插入图片描述

三 、拦截器

1.调用getResponseWithInterceptorChain

Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    // 责任链 倒序调用
    List<Interceptor> interceptors = new ArrayList<>();
    interceptors.addAll(client.interceptors());
    // 5、处理重试与重定向
    interceptors.add(retryAndFollowUpInterceptor);
    // 4、处理 配置请求头等信息
    interceptors.add(new BridgeInterceptor(client.cookieJar()));
    // 3、处理 缓存配置 根据条件(存在响应缓存并被设置为不变的或者响应在有效期内)返回缓存响应
    // 设置请求头(If-None-Match、If-Modified-Since等) 服务器可能返回304(未修改)
    interceptors.add(new CacheInterceptor(client.internalCache()));
    // 2、连接服务器
    interceptors.add(new ConnectInterceptor(client));
    if (!forWebSocket) {
      interceptors.addAll(client.networkInterceptors());
    }
    // 1、执行流操作(写出请求体、获得响应数据)
    //  进行http请求报文的封装与请求报文的解析
    interceptors.add(new CallServerInterceptor(forWebSocket));
    //开始执行类
    Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
        originalRequest, this, eventListener, client.connectTimeoutMillis(),
        client.readTimeoutMillis(), client.writeTimeoutMillis());

    return chain.proceed(originalRequest);
  }

开始分析每一个Interceptor

1.RealInterceptorChain 首先看看(我感觉这个就是一个工厂类,负责调度其他拦截器并返回结果)

执行proceed


    // 创建新的拦截链,链中的拦截器集合index+1
    // Call the next interceptor in the chain.
    RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
        connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
        writeTimeout);
    //   执行当前的拦截器 默认是:retryAndFollowUpInterceptor
    Interceptor interceptor = interceptors.get(index);
    Response response = interceptor.intercept(next);

执行下一个拦截器

2.RetryAndFollowUpInterceptor 重定向拦截器

执行intercept
初始化StreamAllocation
执行个死循环
判断是否关闭。释放请求,抛出异常
然后执行下个拦截器,之后如果是路由异常或者是IO异常的时候去恢复异常,如果返回false抛出异常
否则就继续循环
重试得到一个response
然后进行失败重定向,判断是否是307或者308,请求方法不是get 和 head,返回null,
返回301、302、303、304重新组装Request
判断Request == null 返回Response 否则判断是否与之前的的请求相同,最后重新请求

public Response intercept(Chain chain) throws IOException {
    Request request = chain.request();
    RealInterceptorChain realChain = (RealInterceptorChain) chain;
    Call call = realChain.call();
    EventListener eventListener = realChain.eventListener();
    // 核心 协调连接、请求/响应以及复用  请求寻找并建立流 如果端口号和域名相同就使用同一个socket 。减少tsl三次握手
    StreamAllocation streamAllocation = new StreamAllocation(client.connectionPool(),
        createAddress(request.url()), call, eventListener, callStackTrace);
    this.streamAllocation = streamAllocation;

    int followUpCount = 0;//计数
    Response priorResponse = null;
    while (true) {
      if (canceled) {
        streamAllocation.release();
        throw new IOException("Canceled");
      }

      Response response;
      boolean releaseConnection = true;
      try {
        // 执行责任链 实际上就是下一个拦截器
        response = realChain.proceed(request, streamAllocation, null, null);
        releaseConnection = false;
      } catch (RouteException e) {//路由异常
        // The attempt to connect via a route failed. The request will not have been sent.
        //恢复请求
        if (!recover(e.getLastConnectException(), streamAllocation, false, request)) {
          throw e.getLastConnectException();
        }
        releaseConnection = false;
        continue;//继续重试
      } catch (IOException e) {//IO异常
        // An attempt to communicate with a server failed. The request may have been sent.
        boolean requestSendStarted = !(e instanceof ConnectionShutdownException);
        if (!recover(e, streamAllocation, requestSendStarted, request)) throw e;
        releaseConnection = false;
        continue;//继续重试
      } finally {
        // We're throwing an unchecked exception. Release any resources.
        if (releaseConnection) {
          streamAllocation.streamFailed(null);
          streamAllocation.release();
        }
      }

      // Attach the prior response if it exists. Such responses never have a body.
      //之前重试得到的Response
      if (priorResponse != null) {
        response = response.newBuilder()
            .priorResponse(priorResponse.newBuilder()
                    .body(null)
                    .build())
            .build();
      }


      // 请求失败进行重定向
      Request followUp = followUpRequest(response, streamAllocation.route());

      if (followUp == null) {
        if (!forWebSocket) {
          streamAllocation.release();
        }
        return response;
      }

      closeQuietly(response.body());

      //重定向次数不能大于20
      if (++followUpCount > MAX_FOLLOW_UPS) {
        streamAllocation.release();
        throw new ProtocolException("Too many follow-up requests: " + followUpCount);
      }

      //异常处理
      if (followUp.body() instanceof UnrepeatableRequestBody) {
        streamAllocation.release();
        throw new HttpRetryException("Cannot retry streamed HTTP body", response.code());
      }
      //是否与之前请求连接相同
      if (!sameConnection(response, followUp.url())) {
        streamAllocation.release();
        //重新初始化StreamAllocation
        streamAllocation = new StreamAllocation(client.connectionPool(),
            createAddress(followUp.url()), call, eventListener, callStackTrace);
        this.streamAllocation = streamAllocation;
      } else if (streamAllocation.codec() != null) {
        throw new IllegalStateException("Closing the body of " + response
            + " didn't close its backing stream. Bad interceptor?");
      }
      //得到处理后的Request,继续请求
      request = followUp;
      priorResponse = response;
    }
  }

3.BridgeInterceptor(请求体设置)

这个类主要是设置服务器可以识别的请求体,这里就不细讲了

4.CacheInterceptor 缓存

缓存策略
1.缓存不可用,网络不可用,返回504
2.网络不可用,缓存可用,返回缓存
3.网络可用,请求Response,进行网络缓存,如果返回304,说明缓存有效,合并更新
否则缓存,并且判断是否是get请求,不是则不缓存

 @Override public Response intercept(Chain chain) throws IOException {
    //TODO request对应缓存的Response
    Response cacheCandidate = cache != null
        ? cache.get(chain.request())//request.url为key。获取缓存
        : null;

    long now = System.currentTimeMillis();
    //执行响应缓存策略
    CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();
    Request networkRequest = strategy.networkRequest;//网络请求。如果为null不用进行网络请求
    Response cacheResponse = strategy.cacheResponse;//缓存  如果为空,不使用缓存

    if (cache != null) {//更新统计次数,网络 缓存次数
      cache.trackResponse(strategy);
    }
    // 缓存无效 关闭资源
    if (cacheCandidate != null && cacheResponse == null) {
      closeQuietly(cacheCandidate.body()); // The cache candidate wasn't applicable. Close it.
    }

    // If we're forbidden from using the network and the cache is insufficient, fail.
    //如果既没有网络请求  又没有缓存,返回504错误
    if (networkRequest == null && cacheResponse == null) {
      return new Response.Builder()
          .request(chain.request())
          .protocol(Protocol.HTTP_1_1)
          .code(504)
          .message("Unsatisfiable Request (only-if-cached)")
          .body(Util.EMPTY_RESPONSE)
          .sentRequestAtMillis(-1L)
          .receivedResponseAtMillis(System.currentTimeMillis())
          .build();
    }

    // 网络不可用。使用缓存
    // If we don't need the network, we're done.
    if (networkRequest == null) {
      return cacheResponse.newBuilder()
          .cacheResponse(stripBody(cacheResponse))
          .build();
    }
    // 执行下一个拦截器  进行网络请求
    Response networkResponse = null;
    try {
      networkResponse = chain.proceed(networkRequest);
    } finally {
      // If we're crashing on I/O or otherwise, don't leak the cache body.
      if (networkResponse == null && cacheCandidate != null) {
        closeQuietly(cacheCandidate.body());
      }
    }

    // If we have a cache response too, then we're doing a conditional get.
    // 如果存在缓存 更新
    if (cacheResponse != null) {
      //304响应码 上次请求后,请求需要响应的内容未发生改变 合并更新缓存
      if (networkResponse.code() == HTTP_NOT_MODIFIED) {
        Response response = cacheResponse.newBuilder()
            .headers(combine(cacheResponse.headers(), networkResponse.headers()))
            .sentRequestAtMillis(networkResponse.sentRequestAtMillis())
            .receivedResponseAtMillis(networkResponse.receivedResponseAtMillis())
            .cacheResponse(stripBody(cacheResponse))
            .networkResponse(stripBody(networkResponse))
            .build();
        networkResponse.body().close();

        // Update the cache after combining headers but before stripping the
        // Content-Encoding header (as performed by initContentStream()).
        cache.trackConditionalCacheHit();
        cache.update(cacheResponse, response);
        return response;
      } else {
        closeQuietly(cacheResponse.body());
      }
    }
    //缓存Response
    Response response = networkResponse.newBuilder()
        .cacheResponse(stripBody(cacheResponse))
        .networkResponse(stripBody(networkResponse))
        .build();

    if (cache != null) {
      //判断是否是可缓存
      if (HttpHeaders.hasBody(response) && CacheStrategy.isCacheable(response, networkRequest)) {
        // Offer this request to the cache.
        CacheRequest cacheRequest = cache.put(response);
        return cacheWritingResponse(cacheRequest, response);
      }

      //非get请求无法缓存
      if (HttpMethod.invalidatesCache(networkRequest.method())) {
        try {
          cache.remove(networkRequest);
        } catch (IOException ignored) {
          // The cache cannot be written.
        }
      }
    }

    return response;
  }

CacheStrategy
大致流程:
1.没有缓存,使用网络请求
2 判断是否是https没有握手,执行网络请求
3 检查是否可以缓存
4 请求头是否包含 if-modified-since if-none-match 需要身份验证
5.可缓存,检查有效性,判断是否过期,添加警告信息,
6.缓存过期,添加请求头,进行网络请求

private CacheStrategy getCandidate() {
      // No cached response.
      // TODO 没有缓存
      if (cacheResponse == null) {
        return new CacheStrategy(request, null);
      }

      //TODO 缺少SSL握手信息 删除
      // Drop the cached response if it's missing a required handshake.
      if (request.isHttps() && cacheResponse.handshake() == null) {
        return new CacheStrategy(request, null);
      }

     //不可缓存
      // If this response shouldn't have been stored, it should never be used
      // as a response source. This check should be redundant as long as the
      // persistence store is well-behaved and the rules are constant.
      if (!isCacheable(cacheResponse, request)) {
        return new CacheStrategy(request, null);
      }

      CacheControl requestCaching = request.cacheControl();
      // 请求缓存控制检查 请求头包含If-Modified-Since或者If-None-Match意味着本地缓存过期,需要服务器验证
      if (requestCaching.noCache() || hasConditions(request)) {
        return new CacheStrategy(request, null);
      }

      CacheControl responseCaching = cacheResponse.cacheControl();
      // 响应缓存检查 如果响应缓存设置为不变的 request置为null 直接使用缓存
      if (responseCaching.immutable()) {//缓存
        return new CacheStrategy(null, cacheResponse);
      }


      long ageMillis = cacheResponseAge();
      /**
       * 请求头的检查
       */
      // 响应的最大失效时间
      long freshMillis = computeFreshnessLifetime();
      if (requestCaching.maxAgeSeconds() != -1) {
        freshMillis = Math.min(freshMillis, SECONDS.toMillis(requestCaching.maxAgeSeconds()));
      }

      // 刷新时间 最小有效时间
      long minFreshMillis = 0;
      if (requestCaching.minFreshSeconds() != -1) {
        minFreshMillis = SECONDS.toMillis(requestCaching.minFreshSeconds());
      }

      /**
       * 响应头
       */
      // 最大失效时间
      long maxStaleMillis = 0;
      if (!responseCaching.mustRevalidate() && requestCaching.maxStaleSeconds() != -1) {
        maxStaleMillis = SECONDS.toMillis(requestCaching.maxStaleSeconds());
      }

      // 响应缓存的有效时间检查 在有效期内则使用缓存
      if (!responseCaching.noCache() && ageMillis + minFreshMillis < freshMillis + maxStaleMillis) {
        Response.Builder builder = cacheResponse.newBuilder();
        if (ageMillis + minFreshMillis >= freshMillis) {
          builder.addHeader("Warning", "110 HttpURLConnection \"Response is stale\"");
        }
        long oneDayMillis = 24 * 60 * 60 * 1000L;
        if (ageMillis > oneDayMillis && isFreshnessLifetimeHeuristic()) {
          builder.addHeader("Warning", "113 HttpURLConnection \"Heuristic expiration\"");
        }
        return new CacheStrategy(null, builder.build());
      }

      // Find a condition to add to the request. If the condition is satisfied, the response body
      // will not be transmitted.
      String conditionName;
      String conditionValue;
      // 如果资源未改变请求服务器会返回304 Not Modified
      if (etag != null) {
        // ETags配合使用 验证资源是否改变
        conditionName = "If-None-Match";
        conditionValue = etag;
      } else if (lastModified != null) {
        // lastModified配合使用 验证资源是否改变
        conditionName = "If-Modified-Since";
        conditionValue = lastModifiedString;
      } else if (servedDate != null) {
        // servedDate配合使用 验证资源是否改变
        conditionName = "If-Modified-Since";
        conditionValue = servedDateString;
      } else {
        return new CacheStrategy(request, null); // No condition! Make a regular request.
      }

      Headers.Builder conditionalRequestHeaders = request.headers().newBuilder();
      Internal.instance.addLenient(conditionalRequestHeaders, conditionName, conditionValue);

      Request conditionalRequest = request.newBuilder()
          .headers(conditionalRequestHeaders.build())
          .build();
      return new CacheStrategy(conditionalRequest, cacheResponse);
    }

5.ConnectInterceptor 连接池

代码非常简单,所有相关的请求在StreamAllocation中

@Override 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");
    //连接服务器/复用socket
    HttpCodec httpCodec = streamAllocation.newStream(client, chain, doExtensiveHealthChecks);
    RealConnection connection = streamAllocation.connection();

    return realChain.proceed(request, streamAllocation, httpCodec, connection);
  }

StreamAllocation
1.StreamAllocation:类似一个工厂用来创建连接RealConnection和与远端通信的流的封装对象HttpCodec
2.ConnectionPool:连接池用来存储可用的连接,在条件符合的情况下进行连接复用
3.HttpCodec:对输入输出流的封装对象,对于http1和2实现不同
4.RealConnection:对tcp连接的封装
执行newStream方法

public HttpCodec newStream(
            OkHttpClient client, Interceptor.Chain chain, boolean doExtensiveHealthChecks) {
     

        try {
            // RealConnection 对Socket连接的封装
            // TPC/IP协议是传输层协议,主要解决数据如何在网络中传输
            // Socket则是对TCP/IP协议的封装和应用(程序员层面上)。
            // Http 应用层协议,解决如何包装数据
            // 使用Http协议封装数据,借助TCP/IP协议的实现:Socket 进行数据传输
            RealConnection resultConnection = findHealthyConnection(connectTimeout, readTimeout,
                    writeTimeout, pingIntervalMillis, connectionRetryEnabled,
                    doExtensiveHealthChecks);
            // HttpCodec 处理解析请求与响应的工具类
            HttpCodec resultCodec = resultConnection.newCodec(client, chain, this);

            synchronized (connectionPool) {
                codec = resultCodec;
                return resultCodec;
            }
        } catch (IOException e) {
            throw new RouteException(e);
        }
    }

findHealthyConnection() 中findConnection()
关闭影响新流创建的Socket
判断connection是否可用
没有可用的连接去Internal(OkhttpClient)中连接池中获取
如果没有连接,选择一条路由ip,通过连接池,查找Connection,
如果没有找到创建新的RealConnection连接,并加入连接池中

 private RealConnection findConnection(int connectTimeout, int readTimeout, int writeTimeout,
                                          int pingIntervalMillis, 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;
            //如果现有的链接影响了新的Stream创建,释放资源。关闭socket
            toClose = releaseIfNoNewStreams();
            if (this.connection != null) {
                // We had an already-allocated connection and it's good.
                result = this.connection;
                releasedConnection = null;
            }
            if (!reportedAcquired) {
                // If the connection was never reported acquired, don't report it as released!
                releasedConnection = null;
            }
            // 从连接池中获取连接 (先看put加入连接池)
            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.
        // 路由选择器
        // 有多个ip地址的主机,比如高吞吐量的web服务器(服务器集群)
        // 默认实现为 InetAddress.getAllByName(hostname)
        // 比如  InetAddress[] allByName = InetAddress.getAllByName("www.baidu.com");
        // 将会获得[www.baidu.com/14.215.177.39,www.baidu.com/14.215.177.38]
        // 实际上这里是 从连接池查看 是否有14.215.177.39的闲置连接,没有则查找下一个 14.215.177.38
        // 如果都没有会创建一个 RealConnection 里面包含了 所有的14.215.177.39和14.215.177.38
        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, pingIntervalMillis,
                connectionRetryEnabled, call, eventListener);
        routeDatabase().connected(result.route());

        Socket socket = null;
        synchronized (connectionPool) {
            reportedAcquired = true;
            // 加入连接池
            // Pool the connection.
            //这里ConnectPool会将空闲时间最长的连接移除
            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);

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

ConnectionPool
主要功能是用来缓存连接,当符合条件的时候进行连接复用,内部通过一个队列去缓存连接,当超过缓存时间后会自动清理过期连接

private final int maxIdleConnections;//最大闲置连接数
private final long keepAliveDurationNs;//每个连接最大缓存时间

public ConnectionPool() {
    this(5, 5, TimeUnit.MINUTES);//默认最大缓存5个闲置连接,过期时间5分钟
  }

  public ConnectionPool(int maxIdleConnections, long keepAliveDuration, TimeUnit timeUnit) {
    this.maxIdleConnections = maxIdleConnections;
    this.keepAliveDurationNs = timeUnit.toNanos(keepAliveDuration);

    // Put a floor on the keep alive duration, otherwise cleanup will spin loop.
    if (keepAliveDuration <= 0) {
      throw new IllegalArgumentException("keepAliveDuration <= 0: " + keepAliveDuration);
    }
  }

默认最大缓存5个连接,过期时间为5分钟,存储连接是通过ConnectionPool#put()方法

private final Deque<RealConnection> connections = new ArrayDeque<>();//缓存队列
boolean cleanupRunning;//开启清理过期连接任务标记位
private static final Executor executor = new ThreadPoolExecutor(0 /* corePoolSize */,
      Integer.MAX_VALUE /* maximumPoolSize */, 60L /* keepAliveTime */, TimeUnit.SECONDS,
      new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp ConnectionPool", true));

void put(RealConnection connection) {
    assert (Thread.holdsLock(this));
    if (!cleanupRunning) {
      cleanupRunning = true;
      executor.execute(cleanupRunnable);//在线程池中开启清理过期连接任务
    }
    connections.add(connection);//添加到缓存队列
  }

清理过期连接runnable则是通过cleanup()方法获取最近一个即将过期连接的倒计时,为-1则代表缓存已经清空了直接return退出,否则wait一个即将超时的时间后在进行检查

long cleanup(long now) {
    int inUseConnectionCount = 0;//使用的连接数
    int idleConnectionCount = 0;//闲置的连接数
    RealConnection longestIdleConnection = null;//闲置时间最长的连接
    long longestIdleDurationNs = Long.MIN_VALUE;

    synchronized (this) {
      for (Iterator<RealConnection> i = connections.iterator(); i.hasNext(); ) {
        RealConnection connection = i.next();//遍历缓存的连接

        if (pruneAndGetAllocationCount(connection, now) > 0) {//如果该连接有人使用
          inUseConnectionCount++;//使用连接数++
          continue;
        }

        idleConnectionCount++;//否则闲置连接数++

        long idleDurationNs = now - connection.idleAtNanos;//当前连接闲置时间
        if (idleDurationNs > longestIdleDurationNs) {//找到闲置最久的连接
          longestIdleDurationNs = idleDurationNs;
          longestIdleConnection = connection;
        }
      }

      if (longestIdleDurationNs >= this.keepAliveDurationNs
          || idleConnectionCount > this.maxIdleConnections) {//闲置最久的连接时间超过5分钟或者闲置连接数大于5
        connections.remove(longestIdleConnection);//移除当前连接
      } else if (idleConnectionCount > 0) {//闲置连接数大于0
        return keepAliveDurationNs - longestIdleDurationNs;//return缓存最久连接剩余过期时间
      } else if (inUseConnectionCount > 0) {//如果使用连接数大于0
        return keepAliveDurationNs;//return最大缓存时间
      } else {//没有缓存连接了
        cleanupRunning = false;//把标记位置为false
        return -1;//返回-1停止检测
      }
    }

6.CallServerInterceptor

首先说下HttpCodec 这个方法主要是客户端和服务端操作的IO流,内部通过okio与服务端进行读写
CallServerInterceptor主要是四个步骤。具体这里不详解了

1.写请求头
2.写请求体
3.读响应头
4.读响应体

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值