OkHttp源码分析一从用法来看

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_36391075/article/details/82712533

OkHttp优点

  • 支持HTTP2/SPDY黑科技
  • socket自动选择最好的路线,并支持自动重连
  • 拥有自动维护的socket连接池,减少握手次数
  • 拥有队列线程池,轻松写并发
  • 拥有Interceptors轻松处理请求和响应
  • 基于Headers的缓存策略

OkHttp的使用

一般情况下,我们会这样使用OkHttp:

 OkHttpClient client = new OkHttpClient.Builder().connectTimeout(1000,
                TimeUnit.SECONDS)
                .cookieJar(new CookieJar() {
                    @Override
                    public void saveFromResponse(HttpUrl url, List<Cookie> cookies) {

                    }

                    @Override
                    public List<Cookie> loadForRequest(HttpUrl url) {
                        return null;
                    }
                }).build();
        Request request = new Request.Builder().url("http://www.baidu.com").build();
        Call call = client.newCall(request);
        //异步
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {

            }

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

            }
        });

        //同步
        try {
            Response response = call.execute();
        }catch (IOException e){
            e.printStackTrace();
        }

OkHttpClient

先看它官方对它的描述:

Factory for {@linkplain Call calls}, which can be used to send HTTP requests and read their responses.
它是一个执行调用请求Call的工厂,可以用来发送HTTP请求和读取HTTP的response

 OkHttpClients should be shared
 OKHttpClients应该被共享

OkHttp performs best when you create a single {@code OkHttpClient} instance 
and reuse it for all of your HTTP calls. This is because each client holds its 
own connection pool and thread pools. Reusing connections and threads reduces 
latency and saves memory. Conversely, creating a client for each request 
wastes resources on idle pools.

OKHttp最好只创建一个实例,这样就可以对所有HTTP Call进行复用。
这是因为每一个Client都有自己的连接池和线程池。复用连接池和线程池可以减少延迟和节约内存。
相反地,如果对每个请求都创建一个client,对于空闲的pools会造成浪费资源。

我们一般是这样来创建OKHttpClient的:

new OkHttpClient.Builder().build();

那么我们就先看看这个Builder:

/**
* Builder是一个OKHttpClient的静态内部类
*/
public static final class Builder {
	//调度器,里面包含了线程池和三个队列(readyAsynCalls:保存等待执行的异步请求)
    Dispatcher dispatcher;
    //代理类,默认有三种代理模式:
    //DIRECT(直连),HTTP(http代理),SOCKS(socks代理)
    @Nullable Proxy proxy;
    //协议集合,协议类,用来表示使用的协议版本,比如HTTP 1.0等
    List<Protocol> protocols;
    //连接规范,用于配置Socket连接层,对于HTTPS,还能配置安全传输层协议版本和密码套件
    List<ConnectionSpec> connectionSpecs;
    
    //拦截器
    final List<Interceptor> interceptors = new ArrayList<>();
    final List<Interceptor> networkInterceptors = new ArrayList<>();

    EventListener.Factory eventListenerFactory;
    //代理选择类,默认不适用代理,即使用直连方式
    //我们可以自定义配置,以指定URI使用某种代理,类似代理软件的PAC功能
    ProxySelector proxySelector;
    //Cookie的保存获取
    CookieJar cookieJar;
    //缓存类,内部使用了DiskLruCache来进行管理缓存,
    //匹配缓存的机制不仅仅是根据url,还会根据请求方法和请起头来验证是否可以响应缓存
    //仅支持GET请求的缓存
    @Nullable Cache cache;
    //内置缓存
    @Nullable InternalCache internalCache;
    //Socket的抽象工厂,通过createSocket来创建Socket
    SocketFactory socketFactory;
    //安全套接层工厂,HTTPS相关,用于创建SSLSocket。
    //一般配置HTTPS证书信任问题都需要从这里入手
    @Nullable SSLSocketFactory sslSocketFactory;
    //证书链清洁器,HTTPS相关
    //用于从[Java]的TLS API构建的原始数组中统计有效的证书链,
    //然后清除跟TLS握手不相关的证书,提取可信任的证书以便可以受益于证书锁机制。
    @Nullable CertificateChainCleaner certificateChainCleaner;
    //主机名验证器,与HTTPS中的SSL相关,
    //当握手时如果URL的主机名不是可识别的主机,就会要求进行主机名验证
    HostnameVerifier hostnameVerifier;
    //证书所,HTTPS相关,用于约束哪些证书可以被信任,
    //可以防止一些已知或未知的中间证书机构带来的攻击行为。
    CertificatePinner certificatePinner;
    //身份认证器,当连接提示为授权时,可以通过重新设置请求头来响应一个新的Request
    //状态码401表示远程服务器请求授权,407表示代理服务器请求授权。
    //该认证器在需要时会被RetryAndFollowUpInterceptor触发
    Authenticator proxyAuthenticator;
    Authenticator authenticator;

	//连接池
    ConnectionPool connectionPool;
    Dns dns;
    boolean followSslRedirects;//是否遵循SSL重定向
    boolean followRedirects;//是否重定向
    boolean retryOnConnectionFailure;//失败是否重新连接
    int connectTimeout;//超时时间
    int readTimeout;//读取超时
    int writeTimeout;//写入超时
    int pingInterval;

    public Builder() {
      dispatcher = new Dispatcher();
      protocols = DEFAULT_PROTOCOLS;//默认支持的协议
      connectionSpecs = DEFAULT_CONNECTION_SPECS;//默认的连接规范
      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;
    }
    ... ... 
 }

我们通过源码可以知道Builder只是对OKHttp进行一些配置,在里面有一个Dispatcher,调度器:
先看官方对它的描述:

/**
 * Policy on when async requests are executed.
 * 何时进行异步请求的策略
 *
 * <p>Each dispatcher uses an {@link ExecutorService} to run calls internally. 
 * If you supply your own executor, it should be able to run
 *{@linkplain #getMaxRequests the configured maximum} number
 * of calls concurrently.
 */

Dispatcher是一个异步执行的策略,在Dispatcher中每一个请求都是使用ExecutorService来执行的

public final class Dispatcher {
  private int maxRequests = 64;//最大并发数位64,同时请求
  private int maxRequestsPerHost = 5;//每个主机的最大请求数为5
  private @Nullable Runnable idleCallback;//闲置接口

  /** Executes calls. Created lazily. */
  private @Nullable ExecutorService executorService;//线程池

  /** Ready async calls in the order they'll be run. */
  //已经准备好了的异步请求
  private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();

  /** Running asynchronous calls. Includes canceled calls that haven't finished yet. */
  //正在运行中的异步请求
  private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();

  /** Running synchronous calls. Includes canceled calls that haven't finished yet. */
  //正在运行的同步请求
  private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();

  public Dispatcher(ExecutorService executorService) {
    this.executorService = executorService;
  }

  public Dispatcher() {
  }
... ... 
}

在我们创建OKHttpClient的时候,我们是使用Builder.build()方法:

 public OkHttpClient build() {
      return new OkHttpClient(this);
    }

然后我们使用Call call = client.newCall(request);;来创建一个Call:

 /**
   * Prepares the {@code request} to be executed at some point in the future.
   */
  @Override public Call newCall(Request request) {
    return RealCall.newRealCall(this, request, false /* for web socket */);
  }

 static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
    // Safely publish the Call instance to the EventListener.
    RealCall call = new RealCall(client, originalRequest, forWebSocket);
    call.eventListener = client.eventListenerFactory().create(call);
    return call;
  }

我们可以看到实际上是创建了一个RealCalld的实例,ReadlCall是Call的实现类:

final class RealCall implements Call {
  final OkHttpClient client;
  final RetryAndFollowUpInterceptor retryAndFollowUpInterceptor;

  /**
   * There is a cycle between the {@link Call} and {@link EventListener} that makes this awkward.
   * This will be set after we create the call instance then create the event listener instance.
   */
  private EventListener eventListener;

  /** The application's original request unadulterated by redirects or auth headers. */
  final Request originalRequest;
  final boolean forWebSocket;

  // Guarded by this.
  private boolean executed;

  private RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
    this.client = client;
    this.originalRequest = originalRequest;
    this.forWebSocket = forWebSocket;
    this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);
  }
 ... ...
}

然后我们会执行call.enqueue(new Callback())的方法:实际上是RealCall

  @Override public void enqueue(Callback responseCallback) {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    captureCallStackTrace();
    eventListener.callStart(this);
    client.dispatcher().enqueue(new AsyncCall(responseCallback));
  }

我们可以看到最后是交给了dispatcher:

扫描二维码关注公众号,回复: 3301409 查看本文章
  synchronized void enqueue(AsyncCall call) {
    if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
    //如果真正运行的请求数量小于最大请求数且主机请求数小于最大每个主机请求数
    //加入正在进行异步请求的队列
      runningAsyncCalls.add(call);
      //线程池执行这个请求
      executorService().execute(call);
    } else {
    //加入准备队列
      readyAsyncCalls.add(call);
    }
  }

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

 public static ThreadFactory threadFactory(final String name, final boolean daemon) {
    return new ThreadFactory() {
      @Override public Thread newThread(Runnable runnable) {
        Thread result = new Thread(runnable, name);
        result.setDaemon(daemon);
        return result;
      }
    };
  }

在ThreadPoolExecutor中有三种队列:

  • SynchronousQueue:是无界的,一种无缓冲的等待队列,在某次添加元素后必须等待其他线程取走后才能继续添加,可以认为SynchronousQueue是一个缓存值为1的阻塞队列,但是 isEmpty()方法永远返回是true
  • LinkedBlockingQueue:无界的,一个无界缓存等待队列。基于链表的阻塞队列,内部维持着一个数据缓冲队列, 当生产者往队列中放入一个数据时,队列会从生产者手中获取数据,并缓存在队列内部,而生产者立即返回;只有当队列缓冲区达到最大值缓存容量时(LinkedBlockingQueue可以通过构造函数指定该值),才会阻塞生产者队列,直到消费者从队列中消费掉一份数据,生产者线程会被唤醒,反之对于消费者这端的处理也基于同样的原理。
  • ArrayListBlockingQueue:一个有界缓存的等待队列。基于数组的阻塞队列,同LinkedBlockingQueue类似,内部维持着一个定长数据缓冲队列(该队列由数组构成)。

更多的再百度百度。

回到源码上,在我们执行client.dispatcher().enqueue(new AsyncCall(responseCallback));后,会把请求交给线程池,给线程池传进的是AsyncCall这个的实例,我们知道,在线程池会,最终会调用Runnable的run方法:
那么这个Runnable其实是AsyncCall,我们来看看AsyncCall:

final class AsyncCall extends NamedRunnable {
    private final Callback responseCallback;

    AsyncCall(Callback responseCallback) {
      super("OkHttp %s", redactedUrl());
      this.responseCallback = responseCallback;
    }
    ... ...
  }

/**
 * Runnable implementation which always sets its thread name.
 */
public abstract class NamedRunnable implements Runnable {
  protected final String name;

  public NamedRunnable(String format, Object... args) {
    this.name = Util.format(format, args);
  }

  @Override public final void run() {
    String oldName = Thread.currentThread().getName();
    Thread.currentThread().setName(name);
    try {
      execute();
    } finally {
      Thread.currentThread().setName(oldName);
    }
  }

  protected abstract void execute();
}

我们可以看到AsynCall其实是Runnable的实现类,在run方法中,最终会调用execute方法:

@Override protected void execute() {
      boolean signalledCallback = false;
      try {
        Response response = getResponseWithInterceptorChain();
        if (retryAndFollowUpInterceptor.isCanceled()) {
          signalledCallback = true;
          responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
        } else {
          signalledCallback = true;
          responseCallback.onResponse(RealCall.this, response);
        }
      } catch (IOException e) {
        if (signalledCallback) {
          // Do not signal the callback twice!
          Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);
        } else {
          eventListener.callFailed(RealCall.this, e);
          responseCallback.onFailure(RealCall.this, e);
        }
      } finally {
        client.dispatcher().finished(this);
      }
    }
  }

在这个方法中,我们就看到我们进行网络请求的回调的,onResponse,onFailure等。我们也可以看到重点代码:getResponseWithInterceptorChain();

 Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    List<Interceptor> interceptors = new ArrayList<>();
    interceptors.addAll(client.interceptors());
    interceptors.add(retryAndFollowUpInterceptor);
    interceptors.add(new BridgeInterceptor(client.cookieJar()));
    interceptors.add(new CacheInterceptor(client.internalCache()));
    interceptors.add(new ConnectInterceptor(client));
    if (!forWebSocket) {
      interceptors.addAll(client.networkInterceptors());
    }
    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);
  }

我们可以看到在这个方法中,添加了一堆拦截器,那么拦截器是干嘛的呢?

/**
 * Observes, modifies, and potentially short-circuits requests going out and the corresponding
 * responses coming back in. Typically interceptors add, remove, or transform headers on the request
 * or response.
 * 观察,修改并可能使请求中断并返回相应的response
 * 典型的拦截器在request或response上添加,移除或者转换headers
 */
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();

    Call call();

    int connectTimeoutMillis();

    Chain withConnectTimeout(int timeout, TimeUnit unit);

    int readTimeoutMillis();

    Chain withReadTimeout(int timeout, TimeUnit unit);

    int writeTimeoutMillis();

    Chain withWriteTimeout(int timeout, TimeUnit unit);
  }
}

Interceptor是OKHttp最核心的一个东西,它不仅仅负责拦截请求进行一些额外的处理,它还把实际的网络请求,缓存,透明压缩等功能都统以了起来,每一个功能都只是一个Interceptor,它们再连接成一个Interceptor.Chain,环环相扣,最终圆满完成一次网络请求。

getResponseWithInterceptorChain方法中,我们可以看到,Interceptor.Chain的分布依次是:
这里写图片描述

  1. 在配置OKHttpClient是设置的Interceptor
  2. 负责失败重试以及重定向的RetryAndFollowUpInterceptor
  3. 负责吧用户构造的请求转换为发送到服务器的请求,把服务器返回的响应转换为用户友好的先不要的BridgeInterceptor
  4. 负责读取缓存直接返回,更新缓存的CacheInterceptor
  5. 负责和服务器建立连接的ConnectionInterceptor
  6. 配置OKHttpClient时设置的networkInterceptor
  7. 负责向服务器发送请求数据,从服务器读取响应数据的CallServerInterceptor

责任链模式在Interceptor链条中得到了很好的实践。

在这里,位置决定了功能,最后一个Interceptor一定是负责和服务器实际通讯的,重定向,缓存等一定是在实际通讯之前的。

然后创建Interceptor.chain:

**
 * A concrete interceptor chain that carries the entire interceptor chain: all application
 * interceptors, the OkHttp core, all network interceptors, and finally the network caller.
 * 一个带有整个拦截器的具体拦截器链:所有应用程序的拦截器,OKHttp的核心,所有网络的拦截器,网络的呼叫着
 */
public final class RealInterceptorChain implements Interceptor.Chain {
  private final List<Interceptor> interceptors;
  private final StreamAllocation streamAllocation;
  private final HttpCodec httpCodec;
  private final RealConnection connection;
  private final int index;
  private final Request request;
  private final Call call;
  private final EventListener eventListener;
  private final int connectTimeout;
  private final int readTimeout;
  private final int writeTimeout;
  private int calls;

  public RealInterceptorChain(List<Interceptor> interceptors, StreamAllocation streamAllocation,
      HttpCodec httpCodec, RealConnection connection, int index, Request request, Call call,
      EventListener eventListener, int connectTimeout, int readTimeout, int writeTimeout) {
    this.interceptors = interceptors;
    this.connection = connection;
    this.streamAllocation = streamAllocation;
    this.httpCodec = httpCodec;
    this.index = index;
    this.request = request;
    this.call = call;
    this.eventListener = eventListener;
    this.connectTimeout = connectTimeout;
    this.readTimeout = readTimeout;
    this.writeTimeout = writeTimeout;
  }

然后调用chain.proceed()方法进行网络呼叫


Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
        originalRequest, this, eventListener, client.connectTimeoutMillis(),
        client.readTimeoutMillis(), client.writeTimeoutMillis());
 chain.proceed(originalRequest);


@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 {
    if (index >= interceptors.size()) throw new AssertionError();

    calls++;

    // If we already have a stream, confirm that the incoming request will use it.
    //如何已经有一个流了,那么主机和接口要相同
    if (this.httpCodec != null && !this.connection.supportsUrl(request.url())) {
      throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
          + " must retain the same host and port");
    }

    // If we already have a stream, confirm that this is the only call to chain.proceed().
    //保证只调用一次chain.proceed
    if (this.httpCodec != null && calls > 1) {
      throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
          + " must call proceed() exactly once");
    }

    // Call the next interceptor in the chain.
    //调用chain中的下一个interceptor
    RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
        connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
        writeTimeout);
    Interceptor interceptor = interceptors.get(index);
    //开始挨个调用interceptor
    Response response = interceptor.intercept(next);

    // Confirm that the next interceptor made its required call to chain.proceed().
    if (httpCodec != null && index + 1 < interceptors.size() && next.calls != 1) {
      throw new IllegalStateException("network interceptor " + interceptor
          + " must call proceed() exactly once");
    }

    // Confirm that the intercepted response isn't null.
    if (response == null) {
      throw new NullPointerException("interceptor " + interceptor + " returned null");
    }

    if (response.body() == null) {
      throw new IllegalStateException(
          "interceptor " + interceptor + " returned a response with no body");
    }

    return response;
  }

在看其他拦截器之前,先认识几个类:

HttpCodec


/** Encodes HTTP requests and decodes HTTP responses. */
//编码HTTP请求和解码HTTP的响应
public interface HttpCodec {
  /**
   * The timeout to use while discarding a stream of input data. Since this is used for connection
   * reuse, this timeout should be significantly less than the time it takes to establish a new
   * connection.
   */
   //丢弃输入数据时使用的超时时间,用于连接重用
  int DISCARD_STREAM_TIMEOUT_MILLIS = 100;

  /** Returns an output stream where the request body can be streamed. */
  //返回一个output stream(如果RequestBody可以转为流)
  Sink createRequestBody(Request request, long contentLength);

  /** This should update the HTTP engine's sentRequestMillis field. */
  //写入请求头
  void writeRequestHeaders(Request request) throws IOException;

  /** Flush the request to the underlying socket. */
  //刷新请求
  void flushRequest() throws IOException;

  /** Flush the request to the underlying socket and 
  signal no more bytes will be transmitted. */
  void finishRequest() throws IOException;

  /**
   * Parses bytes of a response header from an HTTP transport.
   *
   * @param expectContinue true to return null if this is an intermediate response with a "100"
   *     response code. Otherwise this method never returns null.
   */
   //读取响应头
  Response.Builder readResponseHeaders(boolean expectContinue) throws IOException;

  /** Returns a stream that reads the response body. */
  //返回ResponseBody
  ResponseBody openResponseBody(Response response) throws IOException;

  /**
   * Cancel this stream. Resources held by this stream will be cleaned up, though not synchronously.
   * That may happen later by the connection pool thread.
   */
  void cancel();
}

可以看到HttpCodec是一个接口,我们大约可以知道,通过writeRequestHeaders开始写入请求头到服务器,createRequestBody用于获取写入流来写入请求体。readResponseHeaders用于读取响应头,openResponseBody
用于打开一个响应头。

StreamAllocation

流分配器,该类用于协调连接,流和请求三者之间的关系。通过调用newStream可以获取一个HttpCodec实现:

public HttpCodec newStream(
      OkHttpClient client, Interceptor.Chain chain, boolean doExtensiveHealthChecks) {
      //获取设置的超时时间
    int connectTimeout = chain.connectTimeoutMillis();
    int readTimeout = chain.readTimeoutMillis();
    int writeTimeout = chain.writeTimeoutMillis();
    int pingIntervalMillis = client.pingIntervalMillis();
    boolean connectionRetryEnabled = client.retryOnConnectionFailure();

    try {
    //得到一个健壮的connection,在这个方法中,如果connection不健壮,将重新建立连接
      RealConnection resultConnection = findHealthyConnection(connectTimeout, readTimeout,
          writeTimeout, pingIntervalMillis, connectionRetryEnabled, doExtensiveHealthChecks);
      HttpCodec resultCodec = resultConnection.newCodec(client, chain, this);

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

重试与重定向拦截器RetryAndFollowUpInterceptor

用来实现重试和重定向功能:

@Override public Response intercept(Chain chain) throws IOException {
    Request request = chain.request();
    RealInterceptorChain realChain = (RealInterceptorChain) chain;
    Call call = realChain.call();
    EventListener eventListener = realChain.eventListener();
	
	//使用client中的connectionPool,所请求的URL创建Address对象,
	//并以此创建StrramAllocation对象
	/**
	*Address描述一个特定服务器地址。
	*/
    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 {
      //调用下一个interceptor
        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.getFirstConnectException();
        }
        releaseConnection = false;
        continue;
      } catch (IOException e) {
        // 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.
      if (priorResponse != null) {
        response = response.newBuilder()
            .priorResponse(priorResponse.newBuilder()
                    .body(null)
                    .build())
            .build();
      }

      Request followUp;
      try {
      //提取响应信息
      //followUpRequest中会提取响应信息,看是否要添加验证头,要重定向或者请求超时
        followUp = followUpRequest(response, streamAllocation.route());
      } catch (IOException e) {
        streamAllocation.release();
        throw e;
      }

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

      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 = 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 = followUp;
      priorResponse = response;
    }
  }

它会利用Interceptor链中后面的Interceptor来获取网络响应。并检查是否为重定向响应。如果不是就将响应返回,若是则做进一步处理。

对于重定向的响应,RetryAndFollowUpInterceptor.intercept()会利用响应的信息创建一个新的请求。并检查新请求的服务器与老地址是否相同,若不相同则会根据新的地址创建Addreess对象及StreamAllocation对象。

RetryAndFollowUpInterceptor通过followUpRequest从响应的信息中提取出重定向的信息,接着通过sameConnection来判断是否需要重定向.

RetryAndFollowUpInterceptor主要做了

  • 创建StreamAllocation,以此传入到后续的Interceptor中
  • 处理重定向的Http响应
  • 重连接

桥接连接器 BridgeInterceptor

/**
 * Bridges from application code to network code. First it builds a network request from a user
 * request. Then it proceeds to call the network. Finally it builds a user response from the network
 * response.
 * application code到network code的桥梁
 */
public final class BridgeInterceptor implements Interceptor {
  private final CookieJar cookieJar;

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


@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 (contentType != null) {
        requestBuilder.header("Content-Type", contentType.toString());
      }

      long contentLength = body.contentLength();
      if (contentLength != -1) {
        requestBuilder.header("Content-Length", Long.toString(contentLength));
        requestBuilder.removeHeader("Transfer-Encoding");
      } else {
        requestBuilder.header("Transfer-Encoding", "chunked");
        requestBuilder.removeHeader("Content-Length");
      }
    }

    if (userRequest.header("Host") == null) {
      requestBuilder.header("Host", hostHeader(userRequest.url(), false));
    }

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

    Response networkResponse = chain.proceed(requestBuilder.build());

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

    Response.Builder responseBuilder = networkResponse.newBuilder()
        .request(userRequest);

    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);
      String contentType = networkResponse.header("Content-Type");
      responseBuilder.body(new RealResponseBody(contentType, -1L, Okio.buffer(responseBody)));
    }

    return responseBuilder.build();
  }

可以看到在BridgeInterceptor中,主要用于用于完善请求头,比如Content-Type、Content-Length、Host、Connection、Accept-Encoding、User-Agent等等,这些请求头不用用户一一设置,如果用户没有设置该库会检查并自动完善。此外,这里会进行加载和回调cookie。

缓存拦截器 CacheInterceptor

/** Serves requests from the cache and writes responses to the cache. */
public final class CacheInterceptor implements Interceptor {
  final InternalCache cache;

  public CacheInterceptor(InternalCache cache) {
    this.cache = cache;
  }

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

    long now = System.currentTimeMillis();
	//根据请求头获取用户指定的的缓存策略,
	//并根据缓存策略来获取networkRequest,cacheResponse。
    CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();
    Request networkRequest = strategy.networkRequest;
    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.
    //被禁止使用网络且缓存不足
    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.
    //如果不需要网络则直接返回从缓存中读取的response
    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) {
      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 = 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);
      }

      if (HttpMethod.invalidatesCache(networkRequest.method())) {
        try {
          cache.remove(networkRequest);
        } catch (IOException ignored) {
          // The cache cannot be written.
        }
      }
    }

    return response;
  }

缓存拦截器,首先根据Request获取缓存的Response,然后根据用于设置的缓存策略来进一步判断缓存的Response是否可用以及是否发送网络请求。如果从网络中读取,此时再次根据缓存策略来决定是否缓存响应。

OKHttp内置封装了一个Cache类,它利用DiskLruCache,用磁盘上的有限的带下空间进行缓存,按照LRU算法进行缓存淘汰。
我们可以在OKHttpClient时设置Cache对象:

public Cache(File directory, long maxSize);

####建立连接 ConnectInterceptor

/** Opens a connection to the target server
* and proceeds to the next interceptor.
* 跟目标服务器建立连接,并且执行下一个interceptor
 */

public final class ConnectInterceptor implements Interceptor {
  public final OkHttpClient client;

  public ConnectInterceptor(OkHttpClient client) {
    this.client = client;
  }

  @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");
    HttpCodec httpCodec = streamAllocation.newStream(client, chain, doExtensiveHealthChecks);
    RealConnection connection = streamAllocation.connection();

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

利用StreamAllocation.newStrema来得到HttpCodec对象,在这个方法中,会通过RealConnection建立与服务器之间的连接:我们重新看看它:

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

    try {
      RealConnection resultConnection = findHealthyConnection(connectTimeout, readTimeout,
          writeTimeout, pingIntervalMillis, connectionRetryEnabled, doExtensiveHealthChecks);
      HttpCodec resultCodec = resultConnection.newCodec(client, chain, this);

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

  /**
   * Finds a connection and returns it if it is healthy. If it is unhealthy the process is repeated
   * until a healthy connection is found.
   * 找到一个健康的connection
   */
  private RealConnection findHealthyConnection(int connectTimeout, int readTimeout,
      int writeTimeout, int pingIntervalMillis, boolean connectionRetryEnabled,
      boolean doExtensiveHealthChecks) throws IOException {
      
    while (true) {
      RealConnection candidate = findConnection(connectTimeout, readTimeout, writeTimeout,
          pingIntervalMillis, connectionRetryEnabled);

      // If this is a brand new connection, we can skip the extensive health checks.
      synchronized (connectionPool) {
        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;
    }
//返回承载新流的连接,优先选择已存在的连接,然后是在连接池中找,最后是重新创建一个连接
 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;
      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;
      }

      if (result == null) {
        // Attempt to get a connection from the pool.
        //如果没有现有的连接,则尝试从连接池里面获取一个。
        //从连接池中获取一个可以到达address的连接,或为null,当route没有被路由过
        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.
    //进行TCP和TLS握手
    //在这个方法中会调用connectSocket方法,
    //然后会调用socket.connect(address, connectTimeout);
    result.connect(connectTimeout, readTimeout, writeTimeout, pingIntervalMillis,
        connectionRetryEnabled, call, eventListener);
    routeDatabase().connected(result.route());

    Socket socket = null;
    synchronized (connectionPool) {
      reportedAcquired = true;

      // Pool the connection.
      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;
  }

实际上建立连接就是创建了一个HttpCodec对象,它将在后面的步骤中被使用,它是对HTTP协议操作的抽象,有两个实现:Http1Codec和Http2Codec,分别对应HTTP 1.1和HTTP2.0的实现。在 Http1Codec 中,它利用 Okio 对 Socket 的读写操作进行封装

回到ConnectInterceptor中,在创建了HttpCodec对象后,并且建立了连接,然后会调用streamAllocation.connection();返回connection对象

然后进入下一个Interceptor

发送和接收数据 CallServerInterceptor

/** This is the last interceptor in the chain. 
*It makes a network call to the server. */
//做网络请求到服务器
public final class CallServerInterceptor implements Interceptor {
  private final boolean forWebSocket;

  public CallServerInterceptor(boolean forWebSocket) {
    this.forWebSocket = forWebSocket;
  }

  @Override public Response intercept(Chain chain) throws IOException {
  
    RealInterceptorChain realChain = (RealInterceptorChain) chain;
    //这些都是之前创建好了的
    HttpCodec httpCodec = realChain.httpStream();
    StreamAllocation streamAllocation = realChain.streamAllocation();
    RealConnection connection = (RealConnection) realChain.connection();
    Request request = realChain.request();

    long sentRequestMillis = System.currentTimeMillis();

	//开始发送请求头
    realChain.eventListener().requestHeadersStart(realChain.call());
    //写入请求头
    httpCodec.writeRequestHeaders(request);
    //发送请求头结束
    realChain.eventListener().requestHeadersEnd(realChain.call(), request);

    Response.Builder responseBuilder = null;
    //如果Http请求有body的话
    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();
        realChain.eventListener().responseHeadersStart(realChain.call());
        responseBuilder = httpCodec.readResponseHeaders(true);
      }

      if (responseBuilder == null) {
        // Write the request body if the "Expect: 100-continue" expectation was met.
        //开始发送body
        realChain.eventListener().requestBodyStart(realChain.call());
        long contentLength = request.body().contentLength();
        CountingSink requestBodyOut =
            new CountingSink(httpCodec.createRequestBody(request, contentLength));
        BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);
		//写入body
        request.body().writeTo(bufferedRequestBody);
        bufferedRequestBody.close();
        //发送body结束
        realChain.eventListener()
            .requestBodyEnd(realChain.call(), requestBodyOut.successfulCount);
      } 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();
      }
    }
	//结束Http请求发送
    httpCodec.finishRequest();

    if (responseBuilder == null) {
	    //读取响应头
      realChain.eventListener().responseHeadersStart(realChain.call());
      responseBuilder = httpCodec.readResponseHeaders(false);
    }

    Response response = responseBuilder
        .request(request)
        .handshake(streamAllocation.connection().handshake())
        .sentRequestAtMillis(sentRequestMillis)
        .receivedResponseAtMillis(System.currentTimeMillis())
        .build();

    int code = response.code();
    if (code == 100) {
      // server sent a 100-continue even though we did not request one.
      // try again to read the actual response
      responseBuilder = httpCodec.readResponseHeaders(false);

      response = responseBuilder
              .request(request)
              .handshake(streamAllocation.connection().handshake())
              .sentRequestAtMillis(sentRequestMillis)
              .receivedResponseAtMillis(System.currentTimeMillis())
              .build();

      code = response.code();
    }

    realChain.eventListener()
            .responseHeadersEnd(realChain.call(), response);

    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 {
      response = response.newBuilder()
      //服务响应体
          .body(httpCodec.openResponseBody(response))
          .build();
    }

    if ("close".equalsIgnoreCase(response.request().header("Connection"))
        || "close".equalsIgnoreCase(response.header("Connection"))) {
      streamAllocation.noNewStreams();
    }

    if ((code == 204 || code == 205) && response.body().contentLength() > 0) {
      throw new ProtocolException(
          "HTTP " + code + " had non-zero Content-Length: " + response.body().contentLength());
    }

    return response;
  }

主干部分:

  1. 向服务器发送request header
  2. 如有有request body,就向服务器发送
  3. 读取response header,先构造一ResponseBuilder
  4. 如果有response body,就在3的基础上加上body构造一个新的Response对象。

CallSercerInterceptor首先将http请求头部发给服务器,如果http请求有body 的话,会再将body发送给服务器,继而通过httpCodec.finishRequest()结束http请求的发送。

请求完成之后,就可以从ResponseBuilder对象中获取到响应数据。在读取body的时候,因为服务器返回的数据可能非常大,所以必须通过数据流的方式来进行访问。

上面是OKHttp异步请求的流程

OKHttp还有同步请求,

 //同步
        try {
            Response response = call.execute();
        }catch (IOException e){
            e.printStackTrace();
        }

@Override public Response execute() throws IOException {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    captureCallStackTrace();
    eventListener.callStart(this);
    try {
    //和异步请求的区别,异步需要看是否需要放入等待队列
    //同步是直接执行,向runningSyncCalls添加请求,不经过线程池了。
    //异步会将请求交给线程池来处理
    //同步会将请求交给我们自己申请的线程处理
      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);
    }
  }

完整流程:
这里写图片描述

参考:https://www.jianshu.com/p/7b29b89cd7b5

猜你喜欢

转载自blog.csdn.net/qq_36391075/article/details/82712533