OKHttp源码解析

源码解析之前先看之前请求过程

 private  OkHttpClient mClient=new OkHttpClient();

    public static final String BASE_URL="http://greatfeng.top/app";

    public void run() throws Exception {
        Request request = new Request.Builder()
                .url(BASE_URL+"/weather?cityname="+"上海")
                .build();

        mClient.newCall(request).enqueue(new Callback() {
            @Override public void onFailure(Call call, IOException e) {
                e.printStackTrace();
            }

            @Override public void onResponse(Call call, Response response) throws IOException {
                if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

                Headers responseHeaders = response.headers();
                for (int i = 0, size = responseHeaders.size(); i < size; i++) {
                    System.out.println(responseHeaders.name(i) + ": " + responseHeaders.value(i));
                }

                System.out.println(response.body().string());
            }
        });
    }

1.0 初始化一个OkHttpClient对象

new OkHttpClient();

1.1. 新建一个对象,会先加载这个类,会初始化这个类的变量和静态代码块

// 默认支持的http协议版本 http 2.0,http 1.1
  static final List<Protocol> DEFAULT_PROTOCOLS = Util.immutableList(
      Protocol.HTTP_2, Protocol.HTTP_1_1);
  // 默认支持的 https,http
  static final List<ConnectionSpec> DEFAULT_CONNECTION_SPECS = Util.immutableList(
      ConnectionSpec.MODERN_TLS, ConnectionSpec.CLEARTEXT);
// //新建一个内部使用的辅助类,基本上就是调用第一个参数的,同名方法,后面的参数作为方法的参数
static {
    Internal.instance = new Internal() {
      @Override public void addLenient(Headers.Builder builder, String line) {
        builder.addLenient(line);
      }
...
...
      @Override public Call newWebSocketCall(OkHttpClient client, Request originalRequest) {
        return new RealCall(client, originalRequest, true);
      }
    };
  }

1.2 执行OkHttpClient构造函数

public OkHttpClient() {
    this(new Builder());
  }
  

1.3 初始化builder的成员变量,执行builder的构造函数

/* 这两个拦截器主要是用来我们对请求过程进行拦截处理,添加一些我们直接的处理,
    如打印请求日志,为每个请求添加相同的请求头字段,如 apikey 等
    两个区别在于interceptors是在和服务器未连接的时候拦截处理,
    networkInterceptors是和服务器建立连接后添加的拦截处理
*/

    final List<Interceptor> interceptors = new ArrayList<>(); // 拦截器
    final List<Interceptor> networkInterceptors = new ArrayList<>(); // 网络拦截器
    @Nullable Cache cache; // 配置缓存的
     @Nullable Proxy proxy; //配置代理服务器
     SocketFactory socketFactory; // http 用于创建 Socket 对象 
     @Nullable SSLSocketFactory sslSocketFactory; // https 用于配置创建 SSLSocket 对象 
 public Builder() {
    // 新建一个 Dispatcher 对象,主要是用来构建线程池分发请求的
      dispatcher = new Dispatcher(); 
      protocols = DEFAULT_PROTOCOLS;  // 协议版本 http 2.0, http 1.1
      connectionSpecs = DEFAULT_CONNECTION_SPECS; // 协议类型 https,http
      /* 
      事件监听器 默认为NONE,如果要对网络请求过程(请求开始,连接 请求结束,
      发送请求头,发送请求体,dns解析)进行监听,可以进行自定义 EventListener
      */
      eventListenerFactory = EventListener.factory(EventListener.NONE); 
      // 设置默认代理选择器,是 sun.net.spi.DefaultProxySelector 
      proxySelector = ProxySelector.getDefault();
      // 处理 cookie 
      cookieJar = CookieJar.NO_COOKIES;
      socketFactory = SocketFactory.getDefault(); // 设置默认的 SocketFactory
      hostnameVerifier = OkHostnameVerifier.INSTANCE; // 配置 https 检测服务器返回的证书信息与域名匹配认证
      certificatePinner = CertificatePinner.DEFAULT; // 配置 https证书校验信息sha256
      // 当有设置代理服务器访问时,代理服务器需要账号密码认证,默认不启用代理
      proxyAuthenticator = Authenticator.NONE;
      // http 认证,有些服务器进行访问时需要账号密码的,如 basic认证 默认为空
      authenticator = Authenticator.NONE;
      // 缓存连接池
      connectionPool = new ConnectionPool();
      // DNS解析器,默认使用系统的,可以自己自定义的,也可以使用阿里的HttpDns
      dns = Dns.SYSTEM;
      // https连接是否重定向
      followSslRedirects = true;
      // http 连接是否重定向
      followRedirects = true;
      // 连接失败是否重试
      retryOnConnectionFailure = true;
      // 连接超时时间
      connectTimeout = 10_000; 
      // 读取服务器数据超时时间
      readTimeout = 10_000;
      // 发送给服务器数据超时时间
      writeTimeout = 10_000;
      // http 2.0 版本新增的功能(用于计算往返时间,执行“ 活性” 检活)。设置间隔多少毫秒执行一次,为 0 默认不启用
      pingInterval = 0; 
    }

1.4. Dispatcher对象的初始化,里面有个线程池,使用懒加载的形式,只有发生第一次请求的时候才会初始化线程池,使用线程池进行管理发送的网络请求的Call

  // 线程池中最大同时请求个数,超过个数得进行排队 。可进行设置
  private int maxRequests = 64;
  // 为避免线程池中请求中被同一个host的请求占满了,同时也是减轻服务器压力,可设置每个host的最大请求数 ,若超过就会进入排队序列
  private int maxRequestsPerHost = 5;
  
  private @Nullable Runnable idleCallback;

  private @Nullable ExecutorService executorService;

 /* 准备执行的异常请求集合,由于设置了线程池中最大同时请求个数,每个host的最大请求数,
    如果超过就会存起来,当正在请求的 Call请求完成了,就会调度该集合里面的 Call 去请求
 */
  private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();

// 正在执行的异步请求 Call集合
  private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();

 // 正在执行的同步请求 Call 集合,只做统计
  private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
  
  // 单例模式,懒加载,线程池
   public synchronized ExecutorService executorService() {
    if (executorService == null) {
      executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
          new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));
    }
    return executorService;
  }
  

1.5. ConnectionPool对象的初始化


  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));

  /** The maximum number of idle connections for each address. */
  private final int maxIdleConnections;
  private final long keepAliveDurationNs;
  private final Runnable cleanupRunnable = new Runnable() {
    @Override public void run() {
      while (true) {
        long waitNanos = cleanup(System.nanoTime());
        if (waitNanos == -1) return;
        if (waitNanos > 0) {
          long waitMillis = waitNanos / 1000000L;
          waitNanos -= (waitMillis * 1000000L);
          synchronized (ConnectionPool.this) {
            try {
              ConnectionPool.this.wait(waitMillis, (int) waitNanos);
            } catch (InterruptedException ignored) {
            }
          }
        }
      }
    }
  };

  private final Deque<RealConnection> connections = new ArrayDeque<>();
  final RouteDatabase routeDatabase = new RouteDatabase();


public ConnectionPool() {
    this(5, 5, TimeUnit.MINUTES);
  }

  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);
    }
  }

1.6. 对应1.2中OKHttpClient构造函数调用,OkHttpClient对象已生成

OkHttpClient(Builder builder) {
    this.dispatcher = builder.dispatcher;
    this.proxy = builder.proxy;
   .......
    this.pingInterval = builder.pingInterval;
  }

2.0 创建Request对象,包含一个网络请求的信息,有请求头,请求体,请求网址等

        Request request = new Request.Builder()
                .url(BASE_URL+"/weather?cityname="+"上海")
                .build();

3.0 进行网络请求

 mClient.newCall(request).enqueue(new Callback() {})

3.1 创建RealCall对象

  @Override public Call newCall(Request request) {
    return new RealCall(this, request, false /* for web socket */);
  }
  
  
  RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
    final EventListener.Factory eventListenerFactory = client.eventListenerFactory();

    this.client = client;
    this.originalRequest = originalRequest;
    this.forWebSocket = forWebSocket;
    this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);

    // TODO(jwilson): this is unsafe publication and not threadsafe.
    this.eventListener = eventListenerFactory.create(this);
  }
  
  

3.2 把请求发送到Dispatcher的线程池队列

  @Override public void enqueue(Callback responseCallback) {
    synchronized (this) {
    //新建的RealCall的executed 变量为false,执行完为ture,这就是RealCall对象不能执行两次的原因
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    captureCallStackTrace();
    // 创建AsyncCall对象加入到线程池
    client.dispatcher().enqueue(new AsyncCall(responseCallback));
  }

3.3 AsyncCall继承NamedRunnable 实现了Runnable接口

  final class AsyncCall extends NamedRunnable {
   
    }
    
    public abstract class NamedRunnable implements Runnable {
 
  @Override public final void run() {
      execute();
  }

  protected abstract void execute();
}

3.4 加入线程池后就会执行在子线程中执行AsyncCall 的execute的方法

  synchronized void enqueue(AsyncCall call) {
    // 1.4已说明
    if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
      runningAsyncCalls.add(call);
      executorService().execute(call);
    } else {
      readyAsyncCalls.add(call);
    }
  }

3.5 execute 调用getResponseWithInterceptorChain方法获取到Response对象,即服务器返回数据

 @Override protected void execute() {
        Response response = getResponseWithInterceptorChain();
        if (retryAndFollowUpInterceptor.isCanceled()) {
          signalledCallback = true;
          // 若请求失败会回调 3.0 中设置Callback onFailure方法
          responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
        } else {
          signalledCallback = true;
          // 若请求成功会回调 3.0 中设置 Callback onResponse方法
          responseCallback.onResponse(RealCall.this, response);
        }
finally {
        client.dispatcher().finished(this);
      }
    
  }
  

3.6 getResponseWithInterceptorChain

// 把之前设置的自定义interceptors,RetryAndFollowUpInterceptor,BridgeInterceptor
//,CacheInterceptor,ConnectInterceptor,自定义的networkInterceptors,CallServerInterceptor对象
// 保存在interceptors里面作为参数进行构造RealInterceptorChain
  Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    List<Interceptor> interceptors = new ArrayList<>();
    // addInterceptor
    interceptors.addAll(client.interceptors());
    interceptors.add(retryAndFollowUpInterceptor);
    interceptors.add(new BridgeInterceptor(client.cookieJar()));
    interceptors.add(new CacheInterceptor(client.internalCache()));
    interceptors.add(new ConnectInterceptor(client));
    // addNetworkInterceptor
    if (!forWebSocket) {
      interceptors.addAll(client.networkInterceptors());
    }
    interceptors.add(new CallServerInterceptor(forWebSocket));
   
    Interceptor.Chain chain = new RealInterceptorChain(
        interceptors, null, null, null, 0, originalRequest);
    return chain.proceed(originalRequest);
  }
  

3.7. interceptors 保存很多拦截器

OkHttpClient Builder有两个添加Interceptor 两者处于不通的调用阶段,我们可以在网络请求的这两个阶段自定义拦截器,做不同的处理如上篇博文提到的HttpLoggingInterceptor HttpCacheInterceptor等

    
    public Builder addInterceptor(Interceptor interceptor) {
      interceptors.add(interceptor);
      return this;
    }
    
 public Builder addNetworkInterceptor(Interceptor interceptor) {
      networkInterceptors.add(interceptor);
      return this;
    }

Interceptor 和 Chain 接口都比较简单

  public interface Interceptor {
  Response intercept(Chain chain) throws IOException;

  interface Chain {
    Request request();

    Response proceed(Request request) throws IOException;

    /**
     * Returns the connection the request will be executed on. This is only available in the chains
     * of network interceptors; for application interceptors this is always null.
     */
    @Nullable Connection connection();
  }
}

3.8 创建RealInterceptorChain

  public RealInterceptorChain(List<Interceptor> interceptors, StreamAllocation streamAllocation,
      HttpCodec httpCodec, RealConnection connection, int index, Request request) {
    this.interceptors = interceptors;
    this.connection = connection;
    this.streamAllocation = streamAllocation;
    this.httpCodec = httpCodec;
    this.index = index;
    this.request = request;
  }

3.9 调用proceed方法

  • proceed方法先进行了数据的校验,数据如果不正确,抛出异常
  • proceed每次都会用之前构造的这个RealInterceptorChain 的interceptors, streamAllocation, httpCodec, connection, index + 1, request 或者传入单个或者多个新的值去替换部分原来的值 从而去构建新的RealInterceptorChain对象,然后获取到第index个Interceptor,调用Interceptor的intercept方法,并把新建的RealInterceptorChain对象作为参数传入
  • 然后Interceptor的intercept方法又会调用上部分传入的RealInterceptorChain对象的proceed方法 这就成了一个循环直到最后一个CallServerInterceptor,没有调用 RealInterceptorChain的proceed方法跳出循环返回服务器返回的Response,请求完成

这个就使得,interceptors中的上一个Interceptor的intercept方法依赖下一个Interceptor的intercept的返回结果,依次调用完interceptors中的所有Interceptor的intercept方法。

@Override public Response proceed(Request request) throws IOException {
    return proceed(request, streamAllocation, httpCodec, connection);
  }

  public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
      RealConnection connection) throws IOException {

    // Call the next interceptor in the chain.
    RealInterceptorChain next = new RealInterceptorChain(
        interceptors, streamAllocation, httpCodec, connection, index + 1, request);
    Interceptor interceptor = interceptors.get(index);
    Response response = interceptor.intercept(next);

    return response;
  }

所以一个正常的请求会依次调用下列Interceptor的intercept方法得到返回Response

4.0 RetryAndFollowUpInterceptor

4.1 在RealCall构造函数中新建RetryAndFollowUpInterceptor 对象

 
// 见 3.1
this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);
// 见 3.6
interceptors.add(retryAndFollowUpInterceptor);

4.2 若未添加自定义Intercept,这是第一个调用的Intercept的intercept

==followUpRequest方法和HTTP协议相关是对HTTP协议的实现==

 @Override public Response intercept(Chain chain) throws IOException {
    Request request = chain.request();
// 新建 StreamAllocation对象 
    streamAllocation = new StreamAllocation(
        client.connectionPool(), createAddress(request.url()), callStackTrace);

    int followUpCount = 0;
    Response priorResponse = null;
    // 无限循环,可以多次重试请求,followUpCount请求次数大于MAX_FOLLOW_UPS(20)会抛出ProtocolException,跳出循环
    while (true) {
    //进行取消请求,跳出循环就是由这个标志位控制的
      if (canceled) {
        streamAllocation.release();
        throw new IOException("Canceled");
      }

      Response response = null;
      boolean releaseConnection = true;
    
      // 把streamAllocation对象传入下一个 BridgeInterceptor
      
        response = ((RealInterceptorChain) chain).proceed(request, streamAllocation, null, null);
        releaseConnection = false;
...
      // Attach the prior response if it exists. Such responses never have a body.
      // 有些请求可能要多次和服务器进行通信才能完成,如代理,认证等,若之前有先前有返回响应体,存储到Response的priorResponse中
      if (priorResponse != null) {
        response = response.newBuilder()
            .priorResponse(priorResponse.newBuilder()
                    .body(null)
                    .build())
            .build();
      }

// 根据服务器返回代码和HTTP协议格式,判断是否需要多次和服务器进行通信
      Request followUp = followUpRequest(response);
// 若不需要,直接返回请求结果,循环结束
      if (followUp == null) {
        if (!forWebSocket) {
          streamAllocation.release();
        }
        return response;
      }
// 关闭这个返回体的流,进行判断重试次数,及异常检查 继续进行下一次循环

...
      closeQuietly(response.body());
      
      if (++followUpCount > MAX_FOLLOW_UPS) {
        streamAllocation.release();
        throw new ProtocolException("Too many follow-up requests: " + followUpCount);
      }

      request = followUp;
      priorResponse = response;
    }
  }

4.3 新建Address对象

  private Address createAddress(HttpUrl url) {
    SSLSocketFactory sslSocketFactory = null;
    HostnameVerifier hostnameVerifier = null;
    CertificatePinner certificatePinner = null;
    if (url.isHttps()) {
      sslSocketFactory = client.sslSocketFactory();
      hostnameVerifier = client.hostnameVerifier();
      certificatePinner = client.certificatePinner();
    }

    return new Address(url.host(), url.port(), client.dns(), client.socketFactory(),
        sslSocketFactory, hostnameVerifier, certificatePinner, client.proxyAuthenticator(),
        client.proxy(), client.protocols(), client.connectionSpecs(), client.proxySelector());
  }

4.4 新建StreamAllocation对象

  public StreamAllocation(ConnectionPool connectionPool, Address address, Object callStackTrace) {
    this.connectionPool = connectionPool;
    this.address = address;
    this.routeSelector = new RouteSelector(address, routeDatabase());
    this.callStackTrace = callStackTrace;
  }
  

4.5 构建RouteSelector

  
    public RouteSelector(Address address, RouteDatabase routeDatabase) {
    this.address = address;
    this.routeDatabase = routeDatabase;

    resetNextProxy(address.url(), address.proxy());
  }
  
  
    /** Prepares the proxy servers to try. */
  private void resetNextProxy(HttpUrl url, Proxy proxy) {
    if (proxy != null) {
      // If the user specifies a proxy, try that and only that.
      proxies = Collections.singletonList(proxy);
    } else {
      // Try each of the ProxySelector choices until one connection succeeds.
      List<Proxy> proxiesOrNull = address.proxySelector().select(url.uri());
      proxies = proxiesOrNull != null && !proxiesOrNull.isEmpty()
          ? Util.immutableList(proxiesOrNull)
          : Util.immutableList(Proxy.NO_PROXY);
    }
    nextProxyIndex = 0;
  }

5.0 BridgeInterceptor

5.1

     //  见3.6
interceptors.add(new BridgeInterceptor(client.cookieJar()));

  public BridgeInterceptor(CookieJar cookieJar) {
    this.cookieJar = cookieJar;
  }

5.2 构建完整的请求,把一些默认的请求头添加上去,默认 Keep-Alive ,对请求进行gzip压缩传输,用okio库中GzipSource进行解压

 @Override public Response intercept(Chain chain) throws IOException {
    Request userRequest = chain.request();
    Request.Builder requestBuilder = userRequest.newBuilder();

    RequestBody body = userRequest.body();
    if (body != null) {
      MediaType contentType = body.contentType();
     ...

    if (userRequest.header("Connection") == null) {
      requestBuilder.header("Connection", "Keep-Alive");
    }

    // If we add an "Accept-Encoding: gzip" header field we're responsible for also decompressing
    // the transfer stream.
    boolean transparentGzip = false;
    if (userRequest.header("Accept-Encoding") == null && userRequest.header("Range") == null) {
      transparentGzip = true;
      requestBuilder.header("Accept-Encoding", "gzip");
    }

    List<Cookie> cookies = cookieJar.loadForRequest(userRequest.url());
    if (!cookies.isEmpty()) {
      requestBuilder.header("Cookie", cookieHeader(cookies));
    }

    if (userRequest.header("User-Agent") == null) {
      requestBuilder.header("User-Agent", Version.userAgent());
    }


// 把构建好的新的请求传入CacheInterceptor替换原来的Request对象
    Response networkResponse = chain.proceed(requestBuilder.build());

    HttpHeaders.receiveHeaders(cookieJar, userRequest.url(), networkResponse.headers());

    if (transparentGzip
        && "gzip".equalsIgnoreCase(networkResponse.header("Content-Encoding"))
        && HttpHeaders.hasBody(networkResponse)) {
      GzipSource responseBody = new GzipSource(networkResponse.body().source());
      Headers strippedHeaders = networkResponse.headers().newBuilder()
          .removeAll("Content-Encoding")
          .removeAll("Content-Length")
          .build();
      responseBuilder.headers(strippedHeaders);
      responseBuilder.body(new RealResponseBody(strippedHeaders, Okio.buffer(responseBody)));
    }

    return responseBuilder.build();
  }

6.0 CacheInterceptor

6.1 Cache的使用见 OkHttp使用介绍
中Cache-Control 字段

// 见3.6 
interceptors.add(new CacheInterceptor(client.internalCache()));
// 默认情况下 Okhttp是没有设置cache和internalCache的
  InternalCache internalCache() {
    return cache != null ? cache.internalCache : internalCache;
  }
  
  public CacheInterceptor(InternalCache cache) {
    this.cache = cache;
  }

6.2 我们考虑设置了Cache的情况

==Cache的情况也是HTTP协议相关是对HTTP协议的实现==

使用的是DiskLruCache作为缓存,磁盘缓存库

  @Override public Response intercept(Chain chain) throws IOException {
    Response cacheCandidate = cache != null
    // 6.3
        ? cache.get(chain.request())
        : null;

    long now = System.currentTimeMillis();
// 6.4
    CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();
    Request networkRequest = strategy.networkRequest;
    Response cacheResponse = strategy.cacheResponse;
    
    // If we don't need the network, we're done.
    if (networkRequest == null) {
      return cacheResponse.newBuilder()
          .cacheResponse(stripBody(cacheResponse))
          .build();
    }

// ...

    Response networkResponse = null;
    try {
    
    
     // 若没有缓存,进入ConnectInterceptor的intercept方法
      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());
      }
    }

  // ...

    return response;
  }

6.3.1

    @Override public Response get(Request request) throws IOException {
      return Cache.this.get(request);
    }

6.3.2

    @Nullable Response get(Request request) {
    String key = key(request.url());
    DiskLruCache.Snapshot snapshot;
    Entry entry;
    try {
      snapshot = cache.get(key);
      if (snapshot == null) {
        return null;
      }
    } catch (IOException e) {
      // Give up because the cache cannot be read.
      return null;
    }

    try {
      entry = new Entry(snapshot.getSource(ENTRY_METADATA));
    } catch (IOException e) {
      Util.closeQuietly(snapshot);
      return null;
    }

    Response response = entry.response(snapshot);

    if (!entry.matches(request, response)) {
      Util.closeQuietly(response.body());
      return null;
    }

    return response;
  }

6.3.3

public Response response(DiskLruCache.Snapshot snapshot) {
      String contentType = responseHeaders.get("Content-Type");
      String contentLength = responseHeaders.get("Content-Length");
      Request cacheRequest = new Request.Builder()
          .url(url)
          .method(requestMethod, null)
          .headers(varyHeaders)
          .build();
      return new Response.Builder()
          .request(cacheRequest)
          .protocol(protocol)
          .code(code)
          .message(message)
          .headers(responseHeaders)
          .body(new CacheResponseBody(snapshot, contentType, contentLength))
          .handshake(handshake)
          .sentRequestAtMillis(sentRequestMillis)
          .receivedResponseAtMillis(receivedResponseMillis)
          .build();
    }
  }

6.4.1

public Factory(long nowMillis, Request request, Response cacheResponse) {
      this.nowMillis = nowMillis;
      this.request = request;
      this.cacheResponse = cacheResponse;

      if (cacheResponse != null) {
        this.sentRequestMillis = cacheResponse.sentRequestAtMillis();
        this.receivedResponseMillis = cacheResponse.receivedResponseAtMillis();
        Headers headers = cacheResponse.headers();
        for (int i = 0, size = headers.size(); i < size; i++) {
          String fieldName = headers.name(i);
          String value = headers.value(i);
          if ("Date".equalsIgnoreCase(fieldName)) {
            servedDate = HttpDate.parse(value);
            servedDateString = value;
          } else if ("Expires".equalsIgnoreCase(fieldName)) {
            expires = HttpDate.parse(value);
          } else if ("Last-Modified".equalsIgnoreCase(fieldName)) {
            lastModified = HttpDate.parse(value);
            lastModifiedString = value;
          } else if ("ETag".equalsIgnoreCase(fieldName)) {
            etag = value;
          } else if ("Age".equalsIgnoreCase(fieldName)) {
            ageSeconds = HttpHeaders.parseSeconds(value, -1);
          }
        }
      }
    }
    

6.4.2

    public CacheStrategy get() {
      CacheStrategy candidate = getCandidate();

      if (candidate.networkRequest != null && request.cacheControl().onlyIfCached()) {
        // We're forbidden from using the network and the cache is insufficient.
        return new CacheStrategy(null, null);
      }

      return candidate;
    }
  private CacheStrategy getCandidate() {
      // No cached response.
      if (cacheResponse == null) {
        return new CacheStrategy(request, null);
      }

      // 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 (requestCaching.noCache() || hasConditions(request)) {
        return new CacheStrategy(request, null);
      }

      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;
      CacheControl responseCaching = cacheResponse.cacheControl();
      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;
      if (etag != null) {
        conditionName = "If-None-Match";
        conditionValue = etag;
      } else if (lastModified != null) {
        conditionName = "If-Modified-Since";
        conditionValue = lastModifiedString;
      } else if (servedDate != null) {
        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);
    }

7.0 ConnectInterceptor

  @Override public Response intercept(Chain chain) throws IOException {
    RealInterceptorChain realChain = (RealInterceptorChain) chain;
    Request request = realChain.request();
    // 见4.2 获取新建的StreamAllocation对象
    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();

    // 把request, streamAllocation, httpCodec, connection传入
    
    //进入CallServerInterceptor的intercept
    return realChain.proceed(request, streamAllocation, httpCodec, connection);
  }
public HttpCodec newStream(OkHttpClient client, boolean doExtensiveHealthChecks) {
// ...

// 7.1.0
      RealConnection resultConnection = findHealthyConnection(connectTimeout, readTimeout,
          writeTimeout, connectionRetryEnabled, doExtensiveHealthChecks);
          
// 7.2.0        
      HttpCodec resultCodec = resultConnection.newCodec(client, this);

      synchronized (connectionPool) {
        codec = resultCodec;
        return resultCodec;
      }

7.1.1

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

     .....

      return candidate;
    }
  }

7.1.2

private RealConnection findConnection(int connectTimeout, int readTimeout, int writeTimeout,
      boolean connectionRetryEnabled) throws IOException {
    Route selectedRoute;
    synchronized (connectionPool) {
  ...

      // Attempt to use an already-allocated connection.
      // 尝试使用自身存储的连接
      RealConnection allocatedConnection = this.connection;
      if (allocatedConnection != null && !allocatedConnection.noNewStreams) {
        return allocatedConnection;
      }

      // Attempt to get a connection from the pool.
      
      //尝试从连接池中获取
      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) {
      if (canceled) throw new IOException("Canceled");

      // Now that we have an IP address, make another attempt at getting a connection from the pool.
      // This could match due to connection coalescing.
      
      //有了selectedRoute后再次从连接池中获取连接
      Internal.instance.get(connectionPool, address, this, selectedRoute);
      if (connection != null) {
        route = selectedRoute;
        return connection;
      }

      // 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;
      
    // 新建连接,并把这个RealConnection对象和StreamAllocation对象绑定
      
      result = new RealConnection(connectionPool, selectedRoute);
      acquire(result);
    }


    // Do TCP + TLS handshakes. This is a blocking operation.
    result.connect(connectTimeout, readTimeout, writeTimeout, connectionRetryEnabled);
    
    Socket socket = null;
    synchronized (connectionPool) {
      // Pool the connection.
      // 把新建的RealConnection对象,放入到连接池
      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;
  }

7.1.3

 public void connect(
      int connectTimeout, int readTimeout, int writeTimeout, boolean connectionRetryEnabled) {
   // ....

    while (true) {
      try {
      // 若为https
        if (route.requiresTunnel()) {
          connectTunnel(connectTimeout, readTimeout, writeTimeout);
        } else {
      //如果为http
      
      // 7.1.3.1.0
          connectSocket(connectTimeout, readTimeout);
        }
    // 7.1.3.2.0
        establishProtocol(connectionSpecSelector);
        break;
      }

 

7.1.3.1.1

==和服务建立socket连接,source,sink是Okio库的IO读写类==,它补充了java.io和java.nio的不足,以便能够更加方便,快速的访问、存储和处理你的数据.

private void connectSocket(int connectTimeout, int readTimeout) throws IOException {
    Proxy proxy = route.proxy();
    Address address = route.address();

    rawSocket = proxy.type() == Proxy.Type.DIRECT || proxy.type() == Proxy.Type.HTTP
        ? address.socketFactory().createSocket() //创建 Socket
        : new Socket(proxy);

    rawSocket.setSoTimeout(readTimeout);
    try {
      Platform.get().connectSocket(rawSocket, route.socketAddress(), connectTimeout); //和服务器建立连接 
    } catch (ConnectException e) {
 //   ...
    }
// ...
      source = Okio.buffer(Okio.source(rawSocket));
      sink = Okio.buffer(Okio.sink(rawSocket));
 // ...  
  }

7.1.3.1.2

public Socket createSocket() {
        return new Socket();
    }

 public void connectSocket(Socket socket, InetSocketAddress address,
      int connectTimeout) throws IOException {
    socket.connect(address, connectTimeout);
  }

7.1.3.2.2

 private void establishProtocol(ConnectionSpecSelector connectionSpecSelector) throws IOException {
    if (route.address().sslSocketFactory() == null) {
      protocol = Protocol.HTTP_1_1;
// 普通http,socket连接
      socket = rawSocket;
      return;
    }

// 构建https连接,暂不分析
    connectTls(connectionSpecSelector);

    if (protocol == Protocol.HTTP_2) {
      socket.setSoTimeout(0); // HTTP/2 connection timeouts are set per-stream.
      http2Connection = new Http2Connection.Builder(true)
          .socket(socket, route.address().url().host(), source, sink)
          .listener(this)
          .build();
      http2Connection.start();
    }
  }

7.2 构建Http1Codec对象

  public HttpCodec newCodec(
      OkHttpClient client, StreamAllocation streamAllocation) throws SocketException {
 // 若为http,http2Connection为null
    if (http2Connection != null) {
      return new Http2Codec(client, streamAllocation, http2Connection);
    } else {
      socket.setSoTimeout(client.readTimeoutMillis());
      source.timeout().timeout(client.readTimeoutMillis(), MILLISECONDS);
      sink.timeout().timeout(client.writeTimeoutMillis(), MILLISECONDS);
      
      // 把source, sink 负责和服务器IO读写流传入
      return new Http1Codec(client, streamAllocation, source, sink);
    }
  }

8.0 CallServerInterceptor

@Override public Response intercept(Chain chain) throws IOException {
    RealInterceptorChain realChain = (RealInterceptorChain) chain;
// ...

// 8.1
    httpCodec.writeRequestHeaders(request);

    Response.Builder responseBuilder = null;
// 8.2  向服务器传输请求体
    if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) {
      // If there's a "Expect: 100-continue" header on the request, wait for a "HTTP/1.1 100
      // Continue" response before transmitting the request body. If we don't get that, return what
      // we did get (such as a 4xx response) without ever transmitting the request body.
      if ("100-continue".equalsIgnoreCase(request.header("Expect"))) {
        httpCodec.flushRequest();
        responseBuilder = httpCodec.readResponseHeaders(true);
      }

      if (responseBuilder == null) {
        // Write the request body if the "Expect: 100-continue" expectation was met.
        Sink requestBodyOut = httpCodec.createRequestBody(request, request.body().contentLength());
        BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);
        request.body().writeTo(bufferedRequestBody);
        bufferedRequestBody.close();
      } else if (!connection.isMultiplexed()) {
        // If the "Expect: 100-continue" expectation wasn't met, prevent the HTTP/1 connection from
        // being reused. Otherwise we're still obligated to transmit the request body to leave the
        // connection in a consistent state.
        streamAllocation.noNewStreams();
      }
    }
// ...  flush()请求
    httpCodec.finishRequest();
// 8.3
    if (responseBuilder == null) {
      responseBuilder = httpCodec.readResponseHeaders(false);
    }

// ...

    int code = response.code();
    if (forWebSocket && code == 101) {
      // Connection is upgrading, but we need to ensure interceptors see a non-null response body.
      response = response.newBuilder()
          .body(Util.EMPTY_RESPONSE)
          .build();
    } else {
// 8.4
      response = response.newBuilder()
          .body(httpCodec.openResponseBody(response))
          .build();
    }

//...

    return response;
  }

==HTTP请求格式==

image

8.1 向服务器传输请求头

  @Override public void writeRequestHeaders(Request request) throws IOException {
    String requestLine = RequestLine.get(
        request, streamAllocation.connection().route().proxy().type());
    writeRequest(request.headers(), requestLine);
  }

public static String get(Request request, Proxy.Type proxyType) {
    StringBuilder result = new StringBuilder();
    result.append(request.method());
    result.append(' ');

    if (includeAuthorityInRequestLine(request, proxyType)) {
      result.append(request.url());
    } else {
      result.append(requestPath(request.url()));
    }

    result.append(" HTTP/1.1");
    return result.toString();
  }
  
  
public void writeRequest(Headers headers, String requestLine) throws IOException {
    if (state != STATE_IDLE) throw new IllegalStateException("state: " + state);
    sink.writeUtf8(requestLine).writeUtf8("\r\n");
    for (int i = 0, size = headers.size(); i < size; i++) {
      sink.writeUtf8(headers.name(i))
          .writeUtf8(": ")
          .writeUtf8(headers.value(i))
          .writeUtf8("\r\n");
    }
    sink.writeUtf8("\r\n");
    state = STATE_OPEN_REQUEST_BODY;
  }

8.3 获取响应头


@Override public Response.Builder readResponseHeaders(boolean expectContinue) throws IOException {
    if (state != STATE_OPEN_REQUEST_BODY && state != STATE_READ_RESPONSE_HEADERS) {
      throw new IllegalStateException("state: " + state);
    }

    try {
      StatusLine statusLine = StatusLine.parse(source.readUtf8LineStrict());

      Response.Builder responseBuilder = new Response.Builder()
          .protocol(statusLine.protocol)
          .code(statusLine.code)
          .message(statusLine.message)
          .headers(readHeaders());

      if (expectContinue && statusLine.code == HTTP_CONTINUE) {
        return null;
      }

      state = STATE_OPEN_RESPONSE_BODY;
      return responseBuilder;
    } catch (EOFException e) {
      // Provide more context if the server ends the stream before sending a response.
      IOException exception = new IOException("unexpected end of stream on " + streamAllocation);
      exception.initCause(e);
      throw exception;
    }
  }

8.4 获取响应体

  @Override public ResponseBody openResponseBody(Response response) throws IOException {
    Source source = getTransferStream(response);
    return new RealResponseBody(response.headers(), Okio.buffer(source));
  }
  
  private Source getTransferStream(Response response) throws IOException {
    if (!HttpHeaders.hasBody(response)) {
      return newFixedLengthSource(0);
    }

    if ("chunked".equalsIgnoreCase(response.header("Transfer-Encoding"))) {
      return newChunkedSource(response.request().url());
    }

    long contentLength = HttpHeaders.contentLength(response);
    if (contentLength != -1) {
      return newFixedLengthSource(contentLength);
    }

    // Wrap the input stream from the connection (rather than just returning
    // "socketIn" directly here), so that we can control its use after the
    // reference escapes.
    return newUnknownLengthSource();
  }

9. 整个网络请求过程已经完成

见3.5 Responsed对象返回回调Callback

// 不管请求成功还是失败都会调用finished方法
finally {
        client.dispatcher().finished(this);
      }
  void finished(AsyncCall call) {
    finished(runningAsyncCalls, call, true);
  }
  private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {
...
...
      if (promoteCalls) promoteCalls();
}

// 每次运行完一个AsyncCall如果运行的个数不超过限制,就添加一个请求进入线程池
  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();

      if (runningCallsForHost(call) < maxRequestsPerHost) {
        i.remove();
        runningAsyncCalls.add(call);
        executorService().execute(call);
      }

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

推荐阅读更多精彩内容