网络请求框架OkHttp3全解系列 - (二)OkHttp的工做流程分析

在本系列的上一篇文章中,咱们学习了OkHttp的基本用法,体验了这个网络加载框架的强大功能,以及它很是简便的API。尚未看过上一篇文章的朋友,建议先去阅读 网络请求框架OkHttp3全解系列 - (一)OkHttp的基本使用java

若是咱们想要进行get请求,那么使用少许的代码就能实现,以下所示:web

OkHttpClient httpClient = new OkHttpClient();
        String url = "https://www.baidu.com/";
        Request getRequest = new Request.Builder()
                .url(url)
                .get()
                .build();
        Call call = httpClient.newCall(getRequest);
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {

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

            }
        });        
复制代码

看起来很简洁的代码,实际是OkHttp在背后帮咱们默默执行了成吨的工做。缓存

知其然也要知其因此然。那么这篇咱们就来解析一下OkHttp的源码,看看它在这些简单用法的背后,到底执行了多么复杂的工做。安全

请求的建立

以上面get请求的代码步骤分析,那么先分析OkHttpClient实例的建立。 在上一篇中,提到OkHttpClient实例化能够直接建立,也可使用Builder建造者模式进行配置而后build()便可。直接建立其实就是使用了默认的配置。其构造方法以下:cookie

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

  OkHttpClient(Builder builder) {
    this.dispatcher = builder.dispatcher;//调度器,用于控制并发的请求。内部保存同步和异步请求的call,并使用线程池处理异步请求。
    this.proxy = builder.proxy;//代理设置
    this.protocols = builder.protocols;//http协议
    this.connectionSpecs = builder.connectionSpecs;//链接配置
    this.interceptors = Util.immutableList(builder.interceptors);//全局拦截器
    this.networkInterceptors = Util.immutableList(builder.networkInterceptors);//网络拦截器
    this.eventListenerFactory = builder.eventListenerFactory;//请求的监听器的建立工厂
    this.proxySelector = builder.proxySelector;//代理选择器
    this.cookieJar = builder.cookieJar;//cookie,默认没有cookie:CookieJar.NO_COOKIES
    this.cache = builder.cache;//网络缓存设置
    this.internalCache = builder.internalCache;//内部使用的缓存接口
    this.socketFactory = builder.socketFactory;//socket工厂

    boolean isTLS = false;
    for (ConnectionSpec spec : connectionSpecs) {
      isTLS = isTLS || spec.isTls();
    }

    if (builder.sslSocketFactory != null || !isTLS) {
      this.sslSocketFactory = builder.sslSocketFactory;
      this.certificateChainCleaner = builder.certificateChainCleaner;
    } else {
      X509TrustManager trustManager = Util.platformTrustManager();
      this.sslSocketFactory = newSslSocketFactory(trustManager);
      this.certificateChainCleaner = CertificateChainCleaner.get(trustManager);
    }

    if (sslSocketFactory != null) {
      Platform.get().configureSslSocketFactory(sslSocketFactory);
    }

    this.hostnameVerifier = builder.hostnameVerifier;
    this.certificatePinner = builder.certificatePinner.withCertificateChainCleaner(
        certificateChainCleaner);
    this.proxyAuthenticator = builder.proxyAuthenticator;
    this.authenticator = builder.authenticator;//以上几个是安全相关设置
    this.connectionPool = builder.connectionPool;//链接池
    this.dns = builder.dns;//域名解析系统
    this.followSslRedirects = builder.followSslRedirects;//ssl重定向
    this.followRedirects = builder.followRedirects;//重定向
    this.retryOnConnectionFailure = builder.retryOnConnectionFailure;//链接失败时是否重试
    this.callTimeout = builder.callTimeout;
    this.connectTimeout = builder.connectTimeout;
    this.readTimeout = builder.readTimeout;
    this.writeTimeout = builder.writeTimeout;//几个超时设置
    this.pingInterval = builder.pingInterval;//ping间隔时间
	//拦截器不能是null
    if (interceptors.contains(null)) {
      throw new IllegalStateException("Null interceptor: " + interceptors);
    }
    if (networkInterceptors.contains(null)) {
      throw new IllegalStateException("Null network interceptor: " + networkInterceptors);
    }
  }

  public static final class Builder {
...
    public Builder() {
    //默认的配置
      dispatcher = new Dispatcher();
      protocols = DEFAULT_PROTOCOLS;
      connectionSpecs = DEFAULT_CONNECTION_SPECS;
      eventListenerFactory = EventListener.factory(EventListener.NONE);
      proxySelector = ProxySelector.getDefault();
      if (proxySelector == null) {
        proxySelector = new NullProxySelector();
      }
      cookieJar = CookieJar.NO_COOKIES;
      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;
      callTimeout = 0;
      connectTimeout = 10_000;
      readTimeout = 10_000;
      writeTimeout = 10_000;
      pingInterval = 0;
    }
...
  }
复制代码

直接建立OkHttpClient实例,配置项就是Builder构造方法中默认值。 可见配置项是很是多的,包括上一篇中已经使用过的超时设置、拦截器。网络

接着看Request的建立,也是使用建造者模式:并发

public Builder() {
      this.method = "GET";
      this.headers = new Headers.Builder();
    }

    public Builder get() {
      return method("GET", null);
    }
    
    public Builder post(RequestBody body) {
      return method("POST", body);
    }

    public Builder method(String method, @Nullable RequestBody body) {
      if (method == null) throw new NullPointerException("method == null");
      if (method.length() == 0) throw new IllegalArgumentException("method.length() == 0");
      if (body != null && !HttpMethod.permitsRequestBody(method)) {
        throw new IllegalArgumentException("method " + method + " must not have a request body.");
      }
      if (body == null && HttpMethod.requiresRequestBody(method)) {
        throw new IllegalArgumentException("method " + method + " must have a request body.");
      }
      this.method = method;
      this.body = body;
      return this;
    }

    public Builder addHeader(String name, String value) {
      headers.add(name, value);
      return this;
    }

    public Builder url(String url) {
      if (url == null) throw new NullPointerException("url == null");
      // Silently replace web socket URLs with HTTP URLs.
      if (url.regionMatches(true, 0, "ws:", 0, 3)) {
        url = "http:" + url.substring(3);
      } else if (url.regionMatches(true, 0, "wss:", 0, 4)) {
        url = "https:" + url.substring(4);
      }
      return url(HttpUrl.get(url));
    }

    public Request build() {
      if (url == null) throw new IllegalStateException("url == null");
      return new Request(this);
    }
复制代码

注意到get()和post(RequestBody body)都是对method方法的包装,method方法内部对请求方式和请求体进行了校验,好比get请求不能有请求体、post请求必需要请求体等。其余比较好理解,再也不赘述。框架

接着看HttpClient的newCall方法:异步

@Override public Call newCall(Request request) {
    return RealCall.newRealCall(this, request, false /* for web socket */);
  }
复制代码

跟进RealCall的newRealCall方法: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.transmitter = new Transmitter(client, call);
    return call;
  }
复制代码

可见HttpClient的newCall方法得到Call实际是RealCall。RealCall就是准备执行的请求,是对接口Call的实现。其内部持有OkHttpClient实例、Request实例。而且这里还建立了Transmitter给RealCall的transmitter赋值。

Transmitter意为发射器,是应用层和网络层的桥梁,在进行 链接、真正发出请求和读取响应中起到很重要的做用,看下构造方法:

public Transmitter(OkHttpClient client, Call call) {
    this.client = client;
    this.connectionPool = Internal.instance.realConnectionPool(client.connectionPool());
    this.call = call;
    this.eventListener = client.eventListenerFactory().create(call);
    this.timeout.timeout(client.callTimeoutMillis(), MILLISECONDS);
  }
复制代码

Transmitter内部持有OkHttpClient、链接池、call、事件监听器。

再回头看RealCall实现的接口Call:

// 已准备要执行的请求。因为表示单个请求/响应对(流),所以没法执行两次
public interface Call extends Cloneable {
  ...
  //同步请求,会阻塞
  Response execute() throws IOException;

  //异步请求
  void enqueue(Callback responseCallback);

  //取消请求,已经完成的不能取消。
  void cancel();
  
  boolean isExecuted();
  boolean isCanceled();
  Timeout timeout();
...
  interface Factory {
    Call newCall(Request request);
  }
}
复制代码

主要是定义请求的执行动做和状态。RealCall对Call的具体实现,在后面执行流程中说明。

好了,请求的建立就到这里了。

请求的调度

执行分为同步和异步,这里先从 同步请求 开始分析,即RealCall的execute方法:

@Override public Response execute() throws IOException {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    transmitter.timeoutEnter();//超时计时开始
    transmitter.callStart();//回调请求监听器的请求开始
    try {
      client.dispatcher().executed(this);//放入队列
      return getResponseWithInterceptorChain();//执行请求获取结果
    } finally {
      client.dispatcher().finished(this);//请求结束
    }
  }
复制代码

首先判断 若是已经执行,就会抛出异常。这就是一个请求只能执行一次的缘由。而后回调请求监听器的请求开始。而后调用client的调度器Dispatcher的executed方法:

synchronized void executed(RealCall call) {
    runningSyncCalls.add(call);
  }
复制代码

很简单,请求放入一个双端队列runningSyncCalls中,表示正在执行的同步请求。

而后返回了getResponseWithInterceptorChain()的结果Response,能够猜到,同步请求真正的请求流程是在getResponseWithInterceptorChain方法中。 最后请求结束,会走Dispatcher的finished(Deque calls, T call)方法,:

//结束 异步请求
  void finished(AsyncCall call) {
  	//callsPerHost-1
    call.callsPerHost().decrementAndGet();
    finished(runningAsyncCalls, call);
  }
  //结束 同步请求
  void finished(RealCall call) {
    finished(runningSyncCalls, call);
  }
  //异步、同步的结束,都会走到这里:从running中移除 并 调用promoteAndExecute
  private <T> void finished(Deque<T> calls, T call) {
    ...
    synchronized (this) {
      //从队列中移除
      if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
      idleCallback = this.idleCallback;
    }

    boolean isRunning = promoteAndExecute();
    ...
  }
复制代码

从队列中移除call,而后执行了 promoteAndExecute(),这里先不跟进去了后面会讲到。

到这里,咱们知道了,同步请求走的是getResponseWithInterceptorChain()方法

咱们再来看 异步请求,即RealCall的enqueue方法:

public void enqueue(Callback responseCallback) {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    transmitter.callStart();//回调请求监听器的请求开始
    client.dispatcher().enqueue(new AsyncCall(responseCallback));//请求调度
  }
复制代码

一样先判断是否已请求过,回调请求开始。而后调用Dispatcher的enqueue方法,参数接受的是AsyncCall,AsyncCall继承NamedRunnable,NamedRunnable实现自Runnable,即AsyncCall就是个Runnable,能够想到它是会在线程或线程池执行run方法的。run方法在AsyncCall没看到啊,实际是在在NamedRunnable中:

//知道当前线程名字的Runnable
public abstract class NamedRunnable implements Runnable {
  protected final String name;

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

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

  protected abstract void execute();
}
复制代码

run调用 抽象方法execute(),execute()在AsyncCall是有实现的,这里先不看。

咱们继续去看Dispatcher的enqueue方法:

void enqueue(AsyncCall call) {
    synchronized (this) {
      //存入等待执行的队列
      readyAsyncCalls.add(call);

      // 相同host的请求,共用一个 调用计数
      if (!call.get().forWebSocket) {
        AsyncCall existingCall = findExistingCallWithHost(call.host());
        if (existingCall != null) call.reuseCallsPerHostFrom(existingCall);
      }
    }
    promoteAndExecute();
  }
  
  //从 runningAsyncCalls或者readyAsyncCalls中找到相同host的请求
  private AsyncCall findExistingCallWithHost(String host) {
    for (AsyncCall existingCall : runningAsyncCalls) {
      if (existingCall.host().equals(host)) return existingCall;
    }
    for (AsyncCall existingCall : readyAsyncCalls) {
      if (existingCall.host().equals(host)) return existingCall;
    }
    return null;
  }
复制代码

先把请求放入双端队列readyAsyncCalls中,表示等待执行的异步请求。为啥是等待执行呢?先留一个疑问。 接着从 正在执行的请求runningAsyncCalls 或 等待执行的请求readyAsyncCalls 中找到是相同host的请求,把callsPerHost重用给当前请求。callsPerHost看名字感受像是 拥有相同host的请求的数量,而且注意到类型是AtomicInteger,声明以下:

private volatile AtomicInteger callsPerHost = new AtomicInteger(0);
复制代码

因此,相同host的请求是共享callsPerHost的,为了后面判断host并发作准备。

继续看,接着调用了promoteAndExecute(),前面看的finish方法也有调用,这里能够跟进看看了:

//调度的核心方法:在 控制异步并发 的策略基础上,使用线程池 执行异步请求
  private boolean promoteAndExecute() {
    assert (!Thread.holdsLock(this));

    List<AsyncCall> executableCalls = new ArrayList<>();
    boolean isRunning;
    synchronized (this) {
      for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
        AsyncCall asyncCall = i.next();

        if (runningAsyncCalls.size() >= maxRequests) break; //最大并发数64
        if (asyncCall.callsPerHost().get() >= maxRequestsPerHost) continue; //Host最大并发数5

        i.remove();//从等待队列中移除
        asyncCall.callsPerHost().incrementAndGet();//Host并发数+1
        executableCalls.add(asyncCall);//加入 可执行请求 的集合
        runningAsyncCalls.add(asyncCall);//加入 正在执行的异步请求队列
      }
      isRunning = runningCallsCount() > 0;//正在执行的异步/同步 请求数 >0
    }

    for (int i = 0, size = executableCalls.size(); i < size; i++) {
      AsyncCall asyncCall = executableCalls.get(i);
      asyncCall.executeOn(executorService());//可执行的请求
    }

    return isRunning;
  }

  public synchronized int runningCallsCount() {
    return runningAsyncCalls.size() + runningSyncCalls.size();
  }
复制代码

遍历readyAsyncCalls,先进行两个检查:正在执行的异步请求runningAsyncCalls数量大于最大并发请求数64就break,或者 相同host请求的数量大于5,就continue。若是检查都经过,就从等待队列中移除,callsPerHost自增1,放入 可执行的集合executableCalls,并添加到队列runningAsyncCalls中,表示正在执行的异步请求。 这里就解释了 异步请求等待队列的意义了,就是为了控制最大并发数的缓冲:异步请求并发数达到6四、相同host的异步请求达到5,都要放入等待队列。

遍历完后 把executableCalls中的请求都走executeOn方法:

void executeOn(ExecutorService executorService) {
      assert (!Thread.holdsLock(client.dispatcher()));
      boolean success = false;
      try {
        executorService.execute(this);//在线程池执行asyncCall
        success = true;
      } catch (RejectedExecutionException e) {
        ...
        transmitter.noMoreExchanges(ioException);
        responseCallback.onFailure(RealCall.this, ioException);//回调失败
      } finally {
        if (!success) {
          client.dispatcher().finished(this); //执行发生异常,结束
        }
      }
    }
    
  //线程池的定义
  public synchronized ExecutorService executorService() {
    if (executorService == null) {
      executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
          new SynchronousQueue<>(), Util.threadFactory("OkHttp Dispatcher", false));
    }
    return executorService;
  }
复制代码

executeOn方法很简单:使用相似CachedThreadPool的线程池 执行请求RealCall。若是执行失败,也会调用dispatcher的finished(Deque calls, T call)方法。

前面分析过,AsyncCall的run方法会走到execute()方法,来看下:

protected void execute() {
      boolean signalledCallback = false;
      transmitter.timeoutEnter();//超时计时开始
      try {
        Response response = getResponseWithInterceptorChain();////执行请求获取结果
        responseCallback.onResponse(RealCall.this, response);//回调结果
      } catch (IOException e) {
        ...
        responseCallback.onFailure(RealCall.this, canceledException);//回调失败
        ...
      } finally {
        client.dispatcher().finished(this);//请求结束
      }
    }
复制代码

咱们发现,这里和 同步请求 就很像了,一样是调用getResponseWithInterceptorChain()方法来获取结果Response,不一样点是使用responseCallback把结果回调出去,最后请求结束也是调用了dispatcher的finish方法。

另外,前面说过,finish方法中也调用了promoteAndExecute()方法,说明 同步/异步 请求 结束后 也会从新调度当前的异步请求。

好了,到这里咱们把 调度流程 梳理下:

  1. 发起 同步 请求后,RealCall使用Dispatcher存入runningSyncCalls,而后使用getResponseWithInterceptorChain()获取结果,最后调用Dispatcher的finish方法结束请求。
  2. 发起 异步 请求后,RealCall使用Dispatcher存入readyAsyncCalls,得到host并发数,使用promoteAndExecute()方法 在 控制异步并发 的策略基础上,使用 线程池 执行异步请求(并发控制有包括 最大并发数6四、host最大并发数5)。异步请求的执行 也是使用getResponseWithInterceptorChain(),得到结果后回调出去。最后调用Dispatcher的finish方法结束请求。
  3. Dispatcher:调度器,主要是异步请求的并发控制、把异步请求放入线程池执行,实现方法是promoteAndExecute()。 promoteAndExecute()有两处调用:添加异步请求时、同步/异步请求 结束时。

请求的执行

重点来啦!

经过上面分析指导,不管同步仍是异步请求,最终的执行都是在RealCall的getResponseWithInterceptorChain()方法,只不过异步请求 须要先经过Dispatcher进行并发控制和线程池处理。那么就来看看getResponseWithInterceptorChain():

Response getResponseWithInterceptorChain() throws IOException {
    List<Interceptor> interceptors = new ArrayList<>();
    interceptors.addAll(client.interceptors()); 		//使用者配置的 应用拦截器,最早拦截
    interceptors.add(new RetryAndFollowUpInterceptor(client));//重试跟进拦截器
    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, transmitter, null, 0,
        originalRequest, this, client.connectTimeoutMillis(),
        client.readTimeoutMillis(), client.writeTimeoutMillis());

    boolean calledNoMoreExchanges = false;
    try {
      Response response = chain.proceed(originalRequest);//链 开始执行
      if (transmitter.isCanceled()) {
        closeQuietly(response);
        throw new IOException("Canceled");
      }
      return response;
    } catch (IOException e) {
      calledNoMoreExchanges = true;
      throw transmitter.noMoreExchanges(e);
    } finally {
      if (!calledNoMoreExchanges) {
        transmitter.noMoreExchanges(null);
      }
    }
  }
复制代码

首先是 把

  • 应用拦截器(外部配置)client.interceptors()、
  • 重试跟进拦截器RetryAndFollowUpInterceptor、
  • 桥拦截器BridgeInterceptor、
  • 缓存拦截器CacheInterceptor、
  • 链接拦截器ConnectInterceptor、
  • 网络拦截器(外部配置)client.networkInterceptors()、
  • 请求服务拦截器CallServerInterceptor,

依次 添加到集合interceptors中。而后使用interceptors、transmitter、originalRequest等建立了拦截器链RealInterceptorChain实例,最后用proceed方法获取到请求的结果Response。

在上一篇 使用方法中有提到拦截器Interceptor,那里配置的拦截器 实际就是 应用拦截器:client.interceptors(),是最先被添加到interceptors中。那么到底 拦截器是个啥呢?chain.proceed是如何获取到结果的呢?不着急,咱们先看看Interceptor类:

//拦截器
public interface Interceptor {

  Response intercept(Chain chain) throws IOException;

  //拦截器链
  interface Chain {
  
    Request request();

	//Chain的核心方法
    Response proceed(Request request) throws IOException;

    //返回请求执行的 链接. 仅网络拦截器可用; 应用拦截器就是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是个接口类,只有一个intercept方法,参数是Chain对象。再注意到 内部接口类Chain -- 拦截器链,有个proceed方法,参数是Request对象,返回值是Response,那么这个方法的实现就是请求的处理过程了。Chain的惟一实现类就是RealInterceptorChain,负责把全部拦截器串联起来,proceed方法就是串联的操做。

上述一系列的拦截器都是Interceptor的实现类,这里先贴出上一篇中实现的应用拦截器(其余拦截器的实现暂不去跟进):

new Interceptor() {
            @Override
            public Response intercept(Chain chain) throws IOException {
                Request request = chain.request();
                String url = request.url().toString();
                Log.i(TAG, "intercept: proceed start: url"+ url+ ", at "+System.currentTimeMillis());
                
                Response response = chain.proceed(request);
                
                ResponseBody body = response.body();
                Log.i(TAG, "intercept: proceed end: url"+ url+ ", at "+System.currentTimeMillis());
                return response;
            }
        }
复制代码

在intercept方法中咱们调用chain.proceed方法获取告终果 并在先后打印了一些日志,那这个Chain实例是哪来的呢?intercept方法啥时候被调用的呢?— — 咱们再回头看getResponseWithInterceptorChain方法,全部拦截器都被传入RealInterceptorChain,能够猜测到,一定是RealInterceptorChain的proceed方法内部调用了拦截器的intercept方法。 那么就来看看吧:

@Override public Response proceed(Request request) throws IOException {
    return proceed(request, transmitter, exchange);
  }

  public Response proceed(Request request, Transmitter transmitter, @Nullable Exchange exchange) 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.exchange != null && !this.exchange.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().
    if (this.exchange != null && calls > 1) {
      throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
          + " must call proceed() exactly once");
    }

    // Call the next interceptor in the chain.
    RealInterceptorChain next = new RealInterceptorChain(interceptors, transmitter, exchange,
        index + 1, request, call, connectTimeout, readTimeout, writeTimeout);
    Interceptor interceptor = interceptors.get(index);
    Response response = interceptor.intercept(next);

    // Confirm that the next interceptor made its required call to chain.proceed().
    if (exchange != 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;
  }
复制代码

在实例化RealInterceptorChain时 index赋值是0,exchange是null,因此前面三个if都没走进去。而后获取了第一个拦截器,也就是咱们配置的应用拦截器,调用了它的interceptor方法,并返回和校验告终果。这里证明了咱们猜测。同时注意到,调用 应用拦截器的interceptor方法传入的参数:拦截器链实例next,next就是把index + 1而已,其余参数和当前实例是同样的。也就是说 在咱们的应用拦截器中调用的是 next的proceed方法。

进一步,next的proceed方法中 一样会获取interceptors的index=1的拦截器,即RetryAndFollowUpInterceptor实例,而后调用其interceptor方法,参数是index+1即index=2的chain。跟进RetryAndFollowUpInterceptor的代码发现,interceptor方法内部也是有调用chain的proceed方法。这样就会依次传递下去,直到最后一个拦截器CallServerInterceptor。

实际上 除了最后一个拦截器CallServerInterceptor以外,全部拦截器的interceptor方法都调用了 传入 chain的proceed方法。每一个拦截器在chain的proceed方法 先后 处理了本身负责的工做。例如咱们的应用拦截器,在chain的proceed方法前 打印了request信息的日志,chain的proceed方法获取结果 以后 打印了response信息的日志。每一个拦截器interceptor方法在 调用chain的proceed方法时 都是为了获取下一个拦截器处理的response,而后返回给上一个拦截器。

逻辑总结以下图:

在这里插入图片描述

这就是 okhttp执行流程的核心了,总体流程以下:

在这里插入图片描述
如今来总结下:

  1. 拦截器链:把原始请求 request 依次 传入到 每一个拦截器。拦截器 处理后 把response 反向 依次 回传。
  2. 拦截器:能够对request进行处理,而后调用index+1的拦截器链proceed方法 获取下一个拦截器处理的结果,接着本身也能够处理这个结果,即: 处理request、chain.proceed、处理response。

不知你有没有发现,这一过程 和 公司工做生产流程 很像:

  1. 老板接到一笔订单,要求10天内生产100台电脑。
  2. 总经理拿到任务后,修改了任务和时间:8天内生产110台,这是基于 生产合格率 以及进行重工、检验、包装、运输的时间上的考量,既要保质保量,也要按时交货。
  3. 任务接着到了部门经理,部门经理先去确认了仓库中是否有足够存货,若是有就直接使用存货来交货,这样不存在任何交货风险(质量、时间);若是没有存货,那么就去要求生产线生产。
  4. 生产线按时按量生产完之后,会把生产状况 上报给部门经理,部门经理把结果总结成excel呈现给总经理,总经理则会把整个生产流程结果及各部门的配合状况,总结成PPT报告给老板。

而不一样的拦截器,在网络请求这一任务中,就扮演着不一样的角色。可能okhttp的做者写拦截器的灵感就来源于生活吧,哈哈。

拦截器 做用
应用拦截器 处理原始请求和最终的响应:能够添加自定义header、通用参数、参数加密、网关接入等等。
RetryAndFollowUpInterceptor 处理错误重试和重定向
BridgeInterceptor 应用层和网络层的桥接拦截器,主要工做是为请求添加cookie、添加固定的header,好比Host、Content-Length、Content-Type、User-Agent等等,而后保存响应结果的cookie,若是响应使用gzip压缩过,则还须要进行解压。
CacheInterceptor 缓存拦截器,获取缓存、更新缓存。若是命中缓存则不会发起网络请求。
ConnectInterceptor 链接拦截器,内部会维护一个链接池,负责链接复用、建立链接(三次握手等等)、释放链接以及建立链接上的socket流。
网络拦截器 用户自定义拦截器,一般用于监控网络层的数据传输。
CallServerInterceptor 请求拦截器,在前置准备工做完成后,真正发起网络请求,进行IO读写。

这里先大概知道每一个拦截器的角色任务,下一篇将会详细分析每一个拦截器,以及重要知识点--缓存和链接池。

那么,咱们对okhttp执行流程的源码分析,到这里也结束了。

总结

如今经过两篇文章,咱们已经掌握了okhttp的基本用法,而且经过阅读源码了解了okhttp总的执行流程——请求的建立、调度、拦截器链处理。接下来的文章,会深刻到每一个拦截器的具体实现,学习okhttp更多的高级使用技巧,感兴趣的朋友请继续关注。

.

欢迎 关注个人 公.众.号

在这里插入图片描述
相关文章
相关标签/搜索