網絡請求框架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更多的高級使用技巧,感興趣的朋友請繼續關注。

.

歡迎 關注個人 公.衆.號

在這裏插入圖片描述
相關文章
相關標籤/搜索