OkHttp 原始碼剖析系列(一)——請求的發起及攔截器機制概述

N0tExpectErr0r發表於2020-01-03

你好,我是 N0tExpectErr0r,一名熱愛技術的 Android 開發

我的個人部落格:blog.N0tExpectErr0r.cn

OkHttp 原始碼剖析系列文章目錄:

OkHttp 原始碼剖析系列(一)——請求的發起及攔截器機制概述

OkHttp 原始碼剖析系列(二)——攔截器整體流程分析

OkHttp 原始碼剖析系列(三)——快取機制

OkHttp 原始碼剖析系列(四)——連線建立概述

OkHttp 原始碼剖析系列(五)——代理路由選擇

OkHttp 原始碼剖析系列(六)——連線複用機制及連線的建立

OkHttp 原始碼剖析系列(七)——請求的發起及響應的讀取

OkHttp 是一個從入門 Android 時就接觸的網路請求庫了,想想現在也陪伴它快兩年了,卻沒有系統性地對它進行過一次系統性的原始碼解析。因此準備開設這樣一個系列,對 OkHttp 的原始碼進行解析。

此篇原始碼解析基於 OkHttp 3.14

OkHttpClient

我們都知道,使用 OkHttp 我們首先需要建立並獲得一個 OkHttpClientOkHttpClient 是 OkHttp 中十分重要的一個類,下面是官方在 Java Doc 中對它的介紹:

Factory for {@linkplain Call calls}, which can be used to send HTTP requests and read their responses.

OkHttpClients should be shared 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.

根據官方對其的介紹可以看出,它是一個 Call 的工廠類,可以用它來生產 Call,從而通過 Call 來發起 HTTP Request 獲取 Response。

同時,官方推薦的使用方式是使用一個全域性的 OkHttpClient 在多個類之間共享。因為每個 Client 都會有一個自己的連線池和執行緒池,複用 Client 可以減少資源的浪費。

它的構建採用了 Builder 模式,提供了許多可供我們配置的引數:

public static final class Builder {
    Dispatcher dispatcher;
    @Nullable
    Proxy proxy;
    List<Protocol> protocols;
    List<ConnectionSpec> connectionSpecs;
    final List<Interceptor> interceptors = new ArrayList<>();
    final List<Interceptor> networkInterceptors = new ArrayList<>();
    EventListener.Factory eventListenerFactory;
    ProxySelector proxySelector;
    CookieJar cookieJar;
    @Nullable
    Cache cache;
    @Nullable
    InternalCache internalCache;
    SocketFactory socketFactory;
    @Nullable
    SSLSocketFactory sslSocketFactory;
    @Nullable
    CertificateChainCleaner certificateChainCleaner;
    HostnameVerifier hostnameVerifier;
    CertificatePinner certificatePinner;
    Authenticator proxyAuthenticator;
    Authenticator authenticator;
    ConnectionPool connectionPool;
    Dns dns;
    boolean followSslRedirects;
    boolean followRedirects;
    boolean retryOnConnectionFailure;
    int callTimeout;
    int connectTimeout;
    int readTimeout;
    int writeTimeout;
    int pingInterval;
    // ...
}
複製程式碼

可以看到,它的可配置的引數還是非常多的。

構建了 OkHttpClient 之後,我們可以通過 OkHttpClient.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 */);
}
複製程式碼

Request

Request 所對應的就是我們 HTTP 請求中的 Request,可以對它的 url、method、header 等在 Builder 中進行配置。

Request 的構建同樣採用了 Builder 模式進行構建:

public static class Builder {
    @Nullable
    HttpUrl url;
    String method;
    Headers.Builder headers;
    @Nullable
    RequestBody body;
    // ...
}
複製程式碼

構建完 Request 後,就可以呼叫 OkHttpClient.newCall 方法建立對應 Call

Call

構建

我們知道,newCall 方法中呼叫了 RealCall.newRealCall(this, request, false /* for web socket */);,其中第三個引數代表是否使用 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;
}
複製程式碼

這裡根據我們傳入的引數構建了一個 RealCall 物件,並根據 client 構建了其 transmitter

RealCall 的建構函式中主要是一些賦值:

private RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
    this.client = client;
    this.originalRequest = originalRequest;
    this.forWebSocket = forWebSocket;
}
複製程式碼

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);
} 
複製程式碼

其中首先呼叫了 Internal.instance.realConnectionPool 方法,通過 client.connectionPool 獲取到了 RealConnectionPool 物件,之後呼叫了 client.eventListenerFactory().create(call) 方法構建立了其 eventListener

請求的發起

OkHttp 的執行有兩種方式,enqueueexecute,它們分別代表了非同步請求與同步請求:

  • enqueue:代表了非同步請求,不會阻塞呼叫執行緒。需要我們傳入一個 Callback,當請求成功時,會回撥其 onResponse 方法,請求失敗時則會回撥其 onFailure 方法。

  • execute:代表了同步請求,會阻塞呼叫執行緒,請求結束後直接返回請求結果。

讓我們分別對其進行分析:

非同步請求

我們先分析一下 enqueue 方法:

@Override
public void enqueue(Callback responseCallback) {
    synchronized (this) {
        if (executed) throw new IllegalStateException("Already Executed");
        executed = true;
    }
    // 通知eventListener
    transmitter.callStart();
    // 構建AsyncCall並分派任務
    client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
複製程式碼

它首先呼叫了 transmitter.callStart,最後呼叫到了之前構造的 eventListenercallStart 方法

之後它呼叫了 client.dispatcher().enqueue 方法,構建了一個 AsyncCall 物件後交給了 client.dispatcher 進行任務的分派。

executeOn

AsyncCall 類對外暴露了 executeOn 方法,Dispatcher 可以通過呼叫該方法並傳入 ExecutorService 使其在該執行緒池所提供的執行緒中發起 HTTP 請求,獲取 Response 並回撥 Callback 的對應方法從而實現任務的排程。

void executeOn(ExecutorService executorService) {
    assert (!Thread.holdsLock(client.dispatcher()));
    boolean success = false;
    try {
    	// 在對應的ExecutorService中執行該AsyncCall
        executorService.execute(this);
        success = true;
    } catch (RejectedExecutionException e) {
   		// 出現問題,呼叫Callback對應方法
        InterruptedIOException ioException = new InterruptedIOException("executor rejected");
        ioException.initCause(e);
        transmitter.noMoreExchanges(ioException);
        responseCallback.onFailure(RealCall.this, ioException);
    } finally {
    	// 不論是否成功,通知Dispatcher請求完成
        if (!success) {
            client.dispatcher().finished(this); // This call is no longer running!
        }
    }
}
複製程式碼

可以看出,AsyncCall 是一個 Runnable,我們看看它實現的 execute 方法:

@Override
protected void execute() {
    boolean signalledCallback = false;
    // 開始Timeout計時
    transmitter.timeoutEnter();
    try {
    	// 獲取Response
        Response response = getResponseWithInterceptorChain();
        signalledCallback = true;
        // 請求成功,通知Callback
        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 {
        	// 請求失敗,通知Callback
            responseCallback.onFailure(RealCall.this, e);
        }
    } finally {
		// 不論是否成功,通知Dispatcher請求完成
        client.dispatcher().finished(this);
    }
}
複製程式碼

這裡首先呼叫了 transmitter.timeoutEnter() 方法開始了 Timeout 的計時。

之後若請求成功,則會通過 getResponseWithInterceptorChain 方法獲取了 Response,之後呼叫 Callback.onResponse 方法通知請求成功。

若請求失敗,會呼叫 Callback.onFailure 方法通知請求失敗。

看來網路請求的核心實現在 getResponseWithInterceptorChain 方法中實現,而 OkHttp 的超時機制與 transmitter.timeoutEnter 有關,我們暫時先不關注這些細節。

非同步執行緒池

讓我們來看看 OkHttp 對非同步請求採用了怎樣的執行緒池。呼叫者在 AsyncCall.executeOn 方法中傳入了 Dispatcher.executorService 方法的返回值,我們來到此方法:

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;
}
複製程式碼

我們知道,這裡的執行緒池是可以通過建立 Dispatcher 時指定的,若不指定,則這裡會建立一個如上程式碼中的執行緒池,我們來分析一下它的幾個引數。

  • 核心執行緒數 corePoolSize:保持線上程池中的執行緒數,即使空閒後也不會保留。由於為 0,因此任何執行緒空閒時都不會被保留。
  • 最大執行緒數 maximumPoolSize:執行緒池最大支援建立的執行緒數,這裡指定了 Integer.MAX_VALUE
  • 執行緒存活時間 keepAliveTime:執行緒空閒後所能存活的時間,若超過該時間就會被回收。這裡指定的時間為 60 個時間單位(60s),也就是說執行緒在空閒超過 60s 後就會被回收。
  • 時間單位 unit:簽名的執行緒存活時間的單位,這裡為 TimeUnit.SECONDS,也就是說秒
  • 執行緒等待佇列 workQueue:執行緒的等待佇列,裡面的元素會按序排隊,依次執行,這裡和指定的是 SynchronousQueue
  • 執行緒工廠 threadFactory:執行緒的建立工廠這裡傳入的是 Util.threadFactory 方法建立的執行緒工廠。

對於上面的幾個引數,我們有幾個細節需要考慮一下:

為什麼要採用 SynchronousQueue

首先我們先需要了解一下什麼是 SynchronousQueue,它雖然是一個佇列,但它內部不存在任何的容器,它採用了一種經典的生產者-消費者模型,它有多個生產者和消費者,當一個生產執行緒進行生產操作(put)時,若沒有消費者執行緒進行消費(take),那麼該執行緒會阻塞,直到有消費者進行消費。也就是說,它僅僅實現了一個傳遞的操作,這種傳遞功能由於沒有了中間的放入容器,再從容器中取出的過程,因此是一種快速傳遞元素的方式,這對於我們網路請求這種高頻請求來說,是十分合適的。關於 SynchronousQueue 可以看這篇文章: java併發之SynchronousQueue實現原理

為什麼執行緒池採用這種執行緒數量不設上限,每個執行緒空閒時只存活很短時間的策略

實際上在 OkHttp 的設計中,將執行緒的個數的維護工作不再交給執行緒池,而是由 Dispatcher 進行實現,通過外部所設定的 maxRequestsmaxRequestsPerHost 來調整等待佇列及執行佇列,從而實現對執行緒最大數量的控制。具體 Dispatcher 的實現在本文後面會講到。

同步請求

execute

我們接著看到 execute 方法,看看同步請求的執行:

@Override
public Response execute() throws IOException {
    synchronized (this) {
        if (executed) throw new IllegalStateException("Already Executed");
        executed = true;
    }
    transmitter.timeoutEnter();
    transmitter.callStart();
    try {
        // 通知Dispatcher
        client.dispatcher().executed(this);
        // 獲取Response
        return getResponseWithInterceptorChain();
    } finally {
    	// 不論是否成功,通知Dispatcher請求完成
        client.dispatcher().finished(this);
    }
}
複製程式碼

它首先呼叫了 Dispatcher.executed 方法,通知 Dispatcher 該 Call 被執行,之後呼叫到了 getResponseWithInterceptorChain 方法獲取 Response,不論是否成功都會呼叫 Dispatcher.finished 通知 Dispatcher 該 Call 執行完成。

Dispatcher 任務排程

enqueue

我們看看 Dispatcher 是如何排程非同步請求的,來到 Dispatcher.enqueue 方法:

void enqueue(AsyncCall call) {
    synchronized (this) {
    	// 加入等待佇列
        readyAsyncCalls.add(call);
        // Mutate the AsyncCall so that it shares the AtomicInteger of an existing running call to
        // the same host.
        if (!call.get().forWebSocket) {
            // 尋找同一個host的Call
            AsyncCall existingCall = findExistingCallWithHost(call.host());
            // 複用Call的callsPerHost
            if (existingCall != null) call.reuseCallsPerHostFrom(existingCall);
        }
    }
    // 嘗試執行等待佇列中的任務
    promoteAndExecute();
}
複製程式碼

這裡先將其加入了 readAsyncCalls 這一等待佇列中。

之後呼叫了 findExistingCallWithHost 方法嘗試尋找 host 相同的 Call,它會遍歷 readyAsyncCallsrunningAsyncCalls 兩個佇列尋找 host 相同的 Call。

若找到了對應的 Call,則會呼叫 call.reuseCallsPerHostFrom 來複用這個 Call 的 callsPerHost,從而便於統計一個 host 對應的 Call 的個數,它是一個 AtomicInteger

最後會呼叫 promoteAndExecute 方法,這個方法會嘗試將等待佇列中的任務執行。

executed

我們繼續看看 Dispatcher 是如何排程同步請求的,來到 Dispatcher.executed 方法:

synchronized void executed(RealCall call) {
    runningSyncCalls.add(call);
}
複製程式碼

這裡很簡單,直接將該同步任務加入了執行佇列 runningSyncCalls 中。

promoteAndExecute

我們看到 promoteAndExecute 方法:

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; // Max capacity.
            if (asyncCall.callsPerHost().get() >= maxRequestsPerHost) continue; // Host max capacity.
            i.remove();
            // 增加host對應執行中call的數量
            asyncCall.callsPerHost().incrementAndGet();
            executableCalls.add(asyncCall);
            runningAsyncCalls.add(asyncCall);
        }
        isRunning = runningCallsCount() > 0;
    }
    for (int i = 0, size = executableCalls.size(); i < size; i++) {
        AsyncCall asyncCall = executableCalls.get(i);
        asyncCall.executeOn(executorService());
    }
    return isRunning;
}
複製程式碼

在這個方法中遍歷了 readyAsyncCalls 佇列,不斷地尋找能夠執行的 AsynCall,若找到則會在最後統一呼叫 AsyncCall.executeOn 方法在自己的 executorService 執行緒池中執行該 Call。其中,執行中的任務不能超過 maxRequests

finished

我們從前面 AsyncCall 的實現可以看出,每次請求完成後,不論成功失敗,都會呼叫到 finished 方法通知 Dispatcher 請求結束:

void finished(AsyncCall call) {
	// 減少host對應執行中call的數量
    call.callsPerHost().decrementAndGet();
    finished(runningAsyncCalls, call);
}
複製程式碼

它呼叫到了 finished 的另一個過載:

private <T> void finished(Deque<T> calls, T call) {
    Runnable idleCallback;
    synchronized (this) {
        if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
        idleCallback = this.idleCallback;
    }
     
    // 嘗試執行等待佇列中的任務
	boolean isRunning = promoteAndExecute();
    if (!isRunning && idleCallback != null) {
        idleCallback.run();
    }
}
複製程式碼

可以看到,這裡再次呼叫了 promoteAndExecute 方法嘗試執行等待佇列中的任務,若當前等待佇列中沒有需要執行的任務,說明目前還比較空閒,沒有到達設定的 maxRequests 。此時會呼叫 idleCallback.run 執行一些空閒 Callback

(這種設計有點類似 HandlerIdleHandler 機制,充分利用了一些空閒資源,值得我們學習)。

小結

可以看出,OkHttp 的任務的排程器的設計將請求分別分至了兩個佇列中,分別是等待佇列及執行佇列。

每次加入新的非同步請求時,都會先將其加入等待佇列,之後遍歷等待佇列嘗試執行等待任務。

每次加入新的同步請求時,都會直接將其加入執行佇列。

而每當一個請求完成時,都會通知到 Dispatcher,Dispatcher 會遍歷準備佇列嘗試執行任務,若沒有執行則說明等待佇列是空的,則會呼叫 idleCallback.run 執行一些空閒時的任務,類似 Handler 的 IdleHandler 機制。

(在多執行緒下載器中的任務排程器就用到了這裡的 Dispatcher 的設計)

響應的獲取

從前面的同步和非同步請求中都可以看出,響應的獲取的核心實現是 RealCall.getResponseWithInterceptorChain 方法:

Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    // 初始化攔截器列表
    List<Interceptor> interceptors = new ArrayList<>();
    // 使用者自定義的 Interceptor
    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) {
    	// 使用者自定義的網路 Interceptor
        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);
        }
    }
}
複製程式碼

這個方法非常重要,短短几行程式碼就實現了對請求的所有處理,它體現了 OkHttp 中一個很重要的核心設計——攔截器機制。

它首先在 interceptors 中加入了使用者自定義的攔截器,之後又按順序分別加入了各種系統內建的攔截器。

之後通過 RealInterceptorChain 的構造 函式構造了一個 Chain 物件,之後呼叫了其 proceed 方法,從而得到了該請求的 Response。

那麼這個過程中究竟是如何獲取到 Response 的呢?讓我們先理解一下 OkHttp 的攔截器機制。

攔截器機制概述

OkHttp 的網路請求的過程就是依賴於各種攔截器(Interceptor)實現的,我們先看看 Interceptor 的定義:

/**
 * 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.
 */
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 實際上是一個介面,裡面只有一個方法 intercept 以及一個介面 Chain

Interceptor

其中,intercept 方法往往是如下的結構:

@Override 
public Response intercept(Chain chain) throws IOException {
	Request request = chain.request();
	// Request階段,該攔截器在Request階段負責做的事情

	// 呼叫RealInterceptorChain.proceed(),其實是在遞迴呼叫下一個攔截器的intercept()方法
	response = ((RealInterceptorChain) chain).proceed(request, streamAllocation, null, null);

	// Response階段,完成了該攔截器在Response階段負責做的事情,然後返回到上一層的攔截器。
	return response;     
}
複製程式碼

這裡先呼叫了 chain.request 方法獲取到了本次請求的 Request 物件,

之後呼叫了 chain.proceed 方法遞迴呼叫下一個攔截器的 interceptor 方法。

最後返回了 chain.proceed 方法所返回的 Response

上面簡單的三行程式碼將整個 intercept 過程分為了兩個階段:

  • Request 階段:執行一些該攔截器在 Request 階段所負責的事情
  • Response 階段:完成該攔截器在 Response 階段所負責的事情

這其實是採用了一種遞迴的設計,類似我們計算機網路中的分層模型,將 OkHttp 的請求分為了幾個階段,分別代表了不同的攔截器,不同攔截器分別會在這個遞迴的過程中有兩次對該請求的處理的可能,一次是在 Request 之前,一次是在 Response 之後,中間的過程中若出現了錯誤,則通過丟擲異常來通知上層。

預置的 Interceptor 有如下幾種:

  • RetryAndFollowUpInterceptor:負責實現重定向功能
  • BridgeInterceptor:將使用者構造的請求轉換為向伺服器傳送的請求,將伺服器返回的響應轉換為對使用者友好的響應
  • CacheInterceptor:讀取快取、更新快取
  • ConnectInterceptor:建立與伺服器的連線
  • CallServerInterceptor:從伺服器讀取響應

可以看出,整個網路請求的過程由各個攔截器互相配合從而實現,通過這種攔截器的機制,可以很方便地調節網路請求的過程及先後順序,同時也能夠很方便地使使用者對其進行擴充套件。

其中使用者可以在兩個時機插入 Interceptor:

  • 網路請求前後:通過 OkHttpClient.addInterceptor 方法新增
  • 讀取響應前後:通過 OkHttpClient.addNetworkInterceptor 方法新增

其整體流程如圖所示:

image-20190730145213711

RealInterceptorChain

我們再看看是如何通過 RealInterceptorChain 將整個攔截器的呼叫過程連線起來的,我們先看看其構造過程:

public RealInterceptorChain(List<Interceptor> interceptors, Transmitter transmitter,
                            @Nullable Exchange exchange, int index, Request request, Call call,
                            int connectTimeout, int readTimeout, int writeTimeout) {
    this.interceptors = interceptors;
    this.transmitter = transmitter;
    this.exchange = exchange;
    this.index = index;
    this.request = request;
    this.call = call;
    this.connectTimeout = connectTimeout;
    this.readTimeout = readTimeout;
    this.writeTimeout = writeTimeout;
}
複製程式碼

這裡只是一些賦值過程,我們接著看到 chain.proceed 方法,看看它是如何執行的:

public Response proceed(Request request, Transmitter transmitter, @Nullable Exchange exchange)
        throws IOException {
   	// ...
   	// 構建下一個Interceptor的Chain
    RealInterceptorChain next = new RealInterceptorChain(interceptors, transmitter, exchange,
            index + 1, request, call, connectTimeout, readTimeout, writeTimeout);
    // 獲取當前Interceptor並執行intercept方法
    Interceptor interceptor = interceptors.get(index);
    Response response = interceptor.intercept(next);
    // ...
    return response;
}
複製程式碼

這裡省略了一些異常處理,可以看到它首先構造了下一個攔截器對應的 Chain,之後獲取到了當前的攔截器並呼叫了其 intercept 方法獲取其結果,在 intercept 方法的引數中傳入的就是下一個攔截器對應的 Chain

通過這種遞迴的設計,從而實現了從上到下,再從下到上這樣一個遞與歸的過程,從而十分漂亮地實現了 HTTP 請求的全過程。

這是一種類似責任鏈模式的實現,這樣的實現在網路請求的過程中十分常見,也十分值得我們去學習。

小結

OkHttp 在讀取響應的過程中採用了一種責任鏈模式,預置了多個負責不同功能的攔截器,將它們通過責任鏈連線在一起,採用了一種遞迴的方式進行呼叫,從而使得每一層在請求前和響應後都能對本次請求作出不同的處理,通過各個攔截器的協調合作,最終完成了整個網路請求的過程。

參考資料

OkHttp 3.x 原始碼解析之Interceptor 攔截器

okhttp之旅(二)--請求與響應流程

java併發之SynchronousQueue實現原理

相關文章