真乾貨!!!網絡請求框架OkHttp3全解系列 - (二)OkHttp的工作流程分析

在本系列的上一篇文章中,我們學習了OkHttp的基本用法,體驗了這個網絡加載框架的強大功能,以及它非常簡便的API。還沒有看過上一篇文章的朋友,建議先去閱讀 網絡請求框架OkHttp3全解系列 - (一)OkHttp的基本使用

如果我們想要進行get請求,那麼使用少量的代碼就能實現,如下所示:

        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()即可。直接創建其實就是使用了默認的配置。其構造方法如下:

  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方法:

  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中,表示正在執行的異步請求。
這裏就解釋了 異步請求等待隊列的意義了,就是爲了控制最大併發數的緩衝:異步請求併發數達到64、相同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()方法 在 控制異步併發 的策略基礎上,使用 線程池 執行異步請求(併發控制有包括 最大併發數64、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更多的高級使用技巧。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章