聲明:原創作品,轉載請註明出處https://www.jianshu.com/p/6a1d4773a130
相信做過Android開發的小夥伴對OkHttp一定不陌生,OkHttp現在是網絡請求最流行的一個庫,出現的很早,早在Android 4.x就出現了,當時這個庫只是對Android原生網絡api進行一個簡單的封裝,後來慢慢的直接廢棄了原生的api,內部直接使用自己開源的okio來實現。OkHttp能成爲如此優秀的一個開源庫,肯定有一些值得學習和借鑑的地方,所以今天就帶大家一起來看看其內部實現細節,當然我不準備一上來就貼上各種源碼,以免搞得暈頭轉向,當然了OkHttp相關的講解網上也有很多文章,所以今天我準備換個思路來寫OkHttp,假如現在是你要實現一個網絡請求庫,你會怎麼做。我們就順着這個思路來看下一個網絡庫或者說一個三方庫是如何一步步實現的。
1.需求分解
首先我們先來明確下,我們這個庫是要做什麼的?很簡單就是實現一個網絡請求操作,說的具體點就是從客戶端發送一個網絡請求到服務端,然後從服務端返回內容到客戶端。如下圖所示:
知道了我們這個庫要幹什麼後,接着我們就要編碼了,嗯??等等這就開始寫代碼了?都不知道要寫什麼?沒錯,可能到這你會感覺不知道要怎麼寫。我們知道Java或者kotlin是一種面向對象的語言,即萬物皆對象。所以在編程時要遵從下面的原則:
原則一:面向對象編程
知道了這個原則,當我們遇到一個需求時,第一步就是拆解需求,也就是分析下我們這個需求中都可以分出哪些對象,有了對象的劃分,就可以方便的寫出對應的class文件。上面圖示中我們可以看到,目前我們已經有的對象有client、request、response、還有個server,當然這個server和我們客戶端沒啥關係,所以這個server就不考慮在內。接着爲了方便管理或者使用,我們把request和response都放入一個叫Call的類中,也就是每來一個網絡請求,client都會創建一個call對象來對server發起請求。
好接下來我們就來看下這個Call類該如何具體實現,說到具體實現,我們這裏還得說下另一個編碼原則,就是:
原則二:面向接口編程,而不是面向具體實現編程
什麼意思呢,就是我們在寫一個類的時候,應該先想想這個類有什麼功能,至於這個功能要怎麼實現先不用管它。
根據這個原則我們來看看這個Call類中有什麼功能,我們上面說了,Call主要功能就是發送request請求到server然後返回響應到client。我們把這個過程定義成一個方法叫做execute,當然我們先不管這個方法怎麼實現也就是我們可以先定義一個Call接口,然後裏面有一個抽象的execute方法。當我們調用這個execute方法,就會把Call裏面的request發送到server,然後返回給我們response。當然這個request我們可以在Call被創建時傳入,或者直接在execute中作爲參數傳入,這裏我們就在Call的構造函數中傳入。好了我們再想想這個Call還有沒有別的功能,有網絡的請求自然而然我們就會想到這個請求應該是可以被取消的,這樣我們就可以定義一個叫做Cancel的抽象方法用來取消這個請求,同理具體的實現要交給具體的實現類,這裏不做實現。當然其實還可以有其他一些功能,只不過這兩個是最基本的功能,這樣我們先來看下這個Call接口:
interface Call{
Response execute()
cancel()
}
好了有Call接口的定義,接下來我們來看下Request類該如何實現,request中的東西就是我們往服務端發送的內容,根據HTTP協議,我們需要知道要向哪個服務器發送數據,也就是需要制定URL,還需要知道我們的請求的請求方法例如post、get或者其他,另外還需要發送HTTP的請求頭head和請求體body。這樣我們簡單歸納下,這個request類中應該含有以下幾個屬性:url、method、head、body,當然還有一些別的這裏暫時不考慮。這樣我們的Request類也有了:
class Request{
HttpUrl url;
String method;
Header header;
RequestBody body;
public Request(HttpUrl url, String method,Header header,RequestBody body){
this.url = url;
this.method = method;
this.header = header;
this.body = body;
}
}
可以看到上面用了HttpUrl、Header、RequestBody幾個類對相應的參數進行了再封裝,這裏就不展開了,這些參數通過構造函數傳入。這樣看上去沒什麼問題,但是像這樣創建一個類需要比較多的參數時我們可以使用建造者模式來優化,具體什麼是建造者模式可以參考我之前寫的這篇文章:建造者模式,使用建造者模式後我們來看下request可以怎樣創建:
Request request = new Request.Builder()
.url(url)
.method(method)
.header(header)
.body(body)
.build()
怎麼樣這樣就很清爽吧,同理返回體Response也用建造者模式創建,這裏就不展開了。
這樣我們創建了Call的接口以及Call中包含的request和response。接下來就可以交給客戶端來調用了。
2.API易用性的設計
那麼客戶端要如何調用呢,這個可以站在我們一個三方庫的易用性來考慮,同理就是先不用管這個庫具體怎麼實現,假如你是一個使用者,你會怎麼設計這個庫的調用方式。通過這種方式考慮,我們很自然的會想到,首先我們需要先創建一個客戶端,然後當我們需要發起一個網絡請求時,需要通過這個客戶端創建一個具體的Call實例對象,然後這個對象的初始化需要傳入一個Request對象。拿到這個Call對象之後,調用它的execute方法來執行具體的網絡請求,然後返回請求結果。這樣的一個調用流程是非常自然且符合我們的調用習慣的。接下來根據我們的想法來看設計下具體的調用方式:
Request request = new Request.Builder()
.url(url)
.head()
.method()
.build()
Client client = new Client()
Call call = client.newCall(request)
Response response = call.execute()
代碼很簡單,也很符合我們的調用習慣,沒錯這個就是官方的調用方式,只不過把Client的名字換成了OkHttpClient。接下來我們通過源碼來看下它的具體實現:
3.OkHttp易用性API背後的實現
首先來看下OkHttpClient類,這個類其實結構很簡單,採用的是建造者模式,裏面很多網絡的配置,比如代理、https、dns等配置,一般我們不需要做更改配置採用默認的就好。我們主要看下Call的創建與調用,首先來看下它的創建過程:
點進newCall
方法:
/**
* 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 */);
}
可以看到newCall方法通過一個叫ReaCall類的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對象,然後給他設置了一個transmitter,這個先不用管它,然後就直接返回了,所以接下來我們再看下這個RealCall具體是怎麼實例化的:
private RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
this.client = client;
this.originalRequest = originalRequest;
this.forWebSocket = forWebSocket;
}
可以看到很簡單,就是把傳入的參數賦值。這幾個參數也很簡單,第一個就是我們的客戶端client,第二個是請求參數,第三個是這個請求是否是一個websocket請求。這個參數在具體發起請求的時候會用到,這裏就先不用管。
到這裏我們Call對象就創建好了,接下來我們就來看下最關鍵的請求執行過程,也就是它的execute方法:
在看execute方法之前,我們先來看下RealCall的大致實現:
final class RealCall implements Call {
final OkHttpClient client;
/**
* There is a cycle between the {@link Call} and {@link Transmitter} that makes this awkward.
* This is set after immediately after creating the call instance.
*/
private Transmitter transmitter;
/** 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;
}
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;
}
@Override public Request request() {
return originalRequest;
}
@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);
}
}
@Override public void enqueue(Callback responseCallback) {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
transmitter.callStart();
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
@Override public void cancel() {
transmitter.cancel();
}
@Override public Timeout timeout() {
return transmitter.timeout();
}
@Override public synchronized boolean isExecuted() {
return executed;
}
@Override public boolean isCanceled() {
return transmitter.isCanceled();
}
@SuppressWarnings("CloneDoesntCallSuperClone") // We are a final type & this saves clearing state.
@Override public RealCall clone() {
return RealCall.newRealCall(client, originalRequest, forWebSocket);
}
final class AsyncCall extends NamedRunnable {
private final Callback responseCallback;
private volatile AtomicInteger callsPerHost = new AtomicInteger(0);
AsyncCall(Callback responseCallback) {
super("OkHttp %s", redactedUrl());
this.responseCallback = responseCallback;
}
AtomicInteger callsPerHost() {
return callsPerHost;
}
void reuseCallsPerHostFrom(AsyncCall other) {
this.callsPerHost = other.callsPerHost;
}
String host() {
return originalRequest.url().host();
}
Request request() {
return originalRequest;
}
RealCall get() {
return RealCall.this;
}
/**
* Attempt to enqueue this async call on {@code executorService}. This will attempt to clean up
* if the executor has been shut down by reporting the call as failed.
*/
void executeOn(ExecutorService executorService) {
assert (!Thread.holdsLock(client.dispatcher()));
boolean success = false;
try {
executorService.execute(this);
success = true;
} catch (RejectedExecutionException e) {
InterruptedIOException ioException = new InterruptedIOException("executor rejected");
ioException.initCause(e);
transmitter.noMoreExchanges(ioException);
responseCallback.onFailure(RealCall.this, ioException);
} finally {
if (!success) {
client.dispatcher().finished(this); // This call is no longer running!
}
}
}
@Override protected void execute() {
boolean signalledCallback = false;
transmitter.timeoutEnter();
try {
Response response = getResponseWithInterceptorChain();
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 {
responseCallback.onFailure(RealCall.this, e);
}
} finally {
client.dispatcher().finished(this);
}
}
}
/**
* Returns a string that describes this call. Doesn't include a full URL as that might contain
* sensitive information.
*/
String toLoggableString() {
return (isCanceled() ? "canceled " : "")
+ (forWebSocket ? "web socket" : "call")
+ " to " + redactedUrl();
}
String redactedUrl() {
return originalRequest.url().redact();
}
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
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);
}
}
}
}
可以看到這個RealCall實現了一個Call接口,我們進入Call看下:
/**
* A call is a request that has been prepared for execution. A call can be canceled. As this object
* represents a single request/response pair (stream), it cannot be executed twice.
*/
public interface Call extends Cloneable {
/** Returns the original request that initiated this call. */
Request request();
/**
* Invokes the request immediately, and blocks until the response can be processed or is in
* error.
*
* <p>To avoid leaking resources callers should close the {@link Response} which in turn will
* close the underlying {@link ResponseBody}.
*
* <pre>{@code
*
* // ensure the response (and underlying response body) is closed
* try (Response response = client.newCall(request).execute()) {
* ...
* }
*
* }</pre>
*
* <p>The caller may read the response body with the response's {@link Response#body} method. To
* avoid leaking resources callers must {@linkplain ResponseBody close the response body} or the
* Response.
*
* <p>Note that transport-layer success (receiving a HTTP response code, headers and body) does
* not necessarily indicate application-layer success: {@code response} may still indicate an
* unhappy HTTP response code like 404 or 500.
*
* @throws IOException if the request could not be executed due to cancellation, a connectivity
* problem or timeout. Because networks can fail during an exchange, it is possible that the
* remote server accepted the request before the failure.
* @throws IllegalStateException when the call has already been executed.
*/
Response execute() throws IOException;
/**
* Schedules the request to be executed at some point in the future.
*
* <p>The {@link OkHttpClient#dispatcher dispatcher} defines when the request will run: usually
* immediately unless there are several other requests currently being executed.
*
* <p>This client will later call back {@code responseCallback} with either an HTTP response or a
* failure exception.
*
* @throws IllegalStateException when the call has already been executed.
*/
void enqueue(Callback responseCallback);
/** Cancels the request, if possible. Requests that are already complete cannot be canceled. */
void cancel();
/**
* Returns true if this call has been either {@linkplain #execute() executed} or {@linkplain
* #enqueue(Callback) enqueued}. It is an error to execute a call more than once.
*/
boolean isExecuted();
boolean isCanceled();
/**
* Returns a timeout that spans the entire call: resolving DNS, connecting, writing the request
* body, server processing, and reading the response body. If the call requires redirects or
* retries all must complete within one timeout period.
*
* <p>Configure the client's default timeout with {@link OkHttpClient.Builder#callTimeout}.
*/
Timeout timeout();
/**
* Create a new, identical call to this one which can be enqueued or executed even if this call
* has already been.
*/
Call clone();
interface Factory {
Call newCall(Request request);
}
}
可以看到,和我們之前定義的差不多,主要有一個execute方法用來執行網絡請求,以及還有cancel,獲取request,是否超時等方法,這裏除了有一個execute網絡請求的方法外另外還有一個叫enqueue的方法,這個也是執行網絡請求,兩者的區別是前者是同步執行,而後者是異步執行。所以這裏我們先來看下enqueue這個異步方法的實現過程,知道這個方法的實現,execute自然而然就知道了。
4.OkHttp是如何實現異步執行的?
同理,這裏我們先不直接看源碼,我們先試着自己實現下。如果是你來實現會怎麼想。
首先異常執行我們自然而然想到的是創建一個線程,在子線程中執行。於是我們就想到用以下方式執行:
public enqueue(Callback callback){
new Thread(new Runnable(){
Response response = execute()
callback.success(response)
}).start()
}
很簡單粗暴,就是直接創建一個新的線程,然後在裏面執行同步的execute方法,然後等執行結束把結果通過callback回調出去。這用寫是可以實現這個異步操作,但是有個問題就是每次要執行網絡請求的時候就創建一個新的線程,這樣就面臨一個問題就是對新創建出來的線程沒有起到統一管理的作用。因此我們自然而然想到用線程池來對線程進行統一管理。JavaAPI自帶原生的線程池,線程池有如下幾個優點:
- 降低資源消耗
- 提高響應速度
- 提高線程的管理性
線程池具體工作方式及實現原理這裏不做過多說明,以下是一個簡單的工作流程:
改用線程池後,我們上面的代碼爲:
ThreadPoolExecutor executor = new TreadPoolExecutor(corePoolSize,maximumPoolSize,keepAliveTime,timeUnit,workQueue,theadFactory)
executor.execute(runnable)
可以看到很簡單創建一個線程池執行器executor,然後調用execute方法並傳入runnable對象,就可以執行了,這裏的runnable裏面執行的就是上面的同步方法execute。
到這裏我們就自己大致實現了下這個異步enqueue方法,不過由於有了線程池的引入,就會多了一些線程調度的東西,比如最大連接數等,這樣我們可以定義一個調度器Dispatcher用來做統一管理,當然這個類是屬於Client中的,起到對Call全局管理作用。
class Dispatcher{
int maxRequest
…
Executor pools
public void enqueue(Runnable runnable){
executor.execute(runnable)
}
}
大致的實現如上。裏面有一個線程池executor,然後有一個enqueue方法,參數爲runnable也就是具體執行請求的實現。
好了接下來,我們來看下源碼中異步請求是如何實現的,我們先來看下OKHttp的異步調用方式:
var repsonse = client.newCall(request).enqueue(object: Callback{
override fun onFailure(call: Call, e: IOException) {
}
override fun onResponse(call: Call, response: Response) {
}
})
它是調用enqueue然後傳入一個回調,這個enqueue其實就是調用的RealCall的enqueue方法,我們進入RealCall的enqueue方法看下:
@Override 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方法,並將傳入的callback回調封裝成了AsyncCall對象傳入進enqueue方法,這個AsyncCall稍後再分析,我們先繼續看這個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) {
AsyncCall existingCall = findExistingCallWithHost(call.host());
if (existingCall != null) call.reuseCallsPerHostFrom(existingCall);
}
}
promoteAndExecute();
}
關鍵的代碼是第一句和最後一句,首先把傳入的AsyncCall傳入一個readyAsyncCalls隊列中,然後最有一句執行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();
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的AsyncCall對象取出來執行他的executeOn方法,這樣我們剛纔的異步執行方法enqueue就執行結束了,那麼現在就需要來看看這個AsyncCall是什麼,以及他的executeOn方法是如何執行的。
final class AsyncCall extends NamedRunnable {
private final Callback responseCallback;
private volatile AtomicInteger callsPerHost = new AtomicInteger(0);
AsyncCall(Callback responseCallback) {
super("OkHttp %s", redactedUrl());
this.responseCallback = responseCallback;
}
AtomicInteger callsPerHost() {
return callsPerHost;
}
void reuseCallsPerHostFrom(AsyncCall other) {
this.callsPerHost = other.callsPerHost;
}
String host() {
return originalRequest.url().host();
}
Request request() {
return originalRequest;
}
RealCall get() {
return RealCall.this;
}
/**
* Attempt to enqueue this async call on {@code executorService}. This will attempt to clean up
* if the executor has been shut down by reporting the call as failed.
*/
void executeOn(ExecutorService executorService) {
assert (!Thread.holdsLock(client.dispatcher()));
boolean success = false;
try {
executorService.execute(this);
success = true;
} catch (RejectedExecutionException e) {
InterruptedIOException ioException = new InterruptedIOException("executor rejected");
ioException.initCause(e);
transmitter.noMoreExchanges(ioException);
responseCallback.onFailure(RealCall.this, ioException);
} finally {
if (!success) {
client.dispatcher().finished(this); // This call is no longer running!
}
}
}
@Override protected void execute() {
boolean signalledCallback = false;
transmitter.timeoutEnter();
try {
Response response = getResponseWithInterceptorChain();
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 {
responseCallback.onFailure(RealCall.this, e);
}
} finally {
client.dispatcher().finished(this);
}
}
}
可以這個AsyncCall是繼承自NamedRunnable類,我們看下NamedRunnable是什麼:
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();
}
是不是有內味了,這個NamedRunnable其實就是一個Runnable,它的run方法裏執行的是execute方法,而這個方法這裏是抽象的,需要子類來實現,它的子類就是我們剛看到的AsyncCall類,所以再回到AsyncCall中看下,首先我們來看下這個類的executeOn方法:
void executeOn(ExecutorService executorService) {
assert (!Thread.holdsLock(client.dispatcher()));
boolean success = false;
try {
executorService.execute(this);
success = true;
} catch (RejectedExecutionException e) {
InterruptedIOException ioException = new InterruptedIOException("executor rejected");
ioException.initCause(e);
transmitter.noMoreExchanges(ioException);
responseCallback.onFailure(RealCall.this, ioException);
} finally {
if (!success) {
client.dispatcher().finished(this); // This call is no longer running!
}
}
}
這裏從上下文知道executeOn方法傳入了一個線程池executorService對象,然後調用線程池executorService的execute方法,線程池的execute方法需要傳入runnable對象,這裏自己就是繼承自runnable,所以直接傳入this,這樣就會調用我們剛說的execute方法,我們來看下execute方法的實現:
@Override protected void execute() {
boolean signalledCallback = false;
transmitter.timeoutEnter();
try {
Response response = getResponseWithInterceptorChain();
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 {
responseCallback.onFailure(RealCall.this, e);
}
} finally {
client.dispatcher().finished(this);
}
}
可以看到這裏調用getResponseWithInterceptorChain方法來獲取請求的結果然後返回,到這裏也就完成了OkHttp的整個異步調用過程。
5.OkHttp中的鏈式調用
接下來網絡執行流程重點就在getResponseWithInterceptorChain方法中了。由於getResponseWithInterceptorChain方法內部採用責任鏈的設計模式,所以在看這個方法之前我們先來簡單看下什麼是責任鏈模式,
如上圖所示,在一個公司裏,boss從客戶那接到一個項目,然後boss把這個項目下發到leader1手中,leader1把任務下發到leader2手中,leader2再把這個項目下發到具體執行的程序員手中,然後程序員完成項目後交付給leader2,leader2再交付給leader1,leader1最後交付給boss。可以看到,這樣的層級結構就像是一個鏈條,我們的任務在這個鏈條上傳遞執行,所以這樣的工作流程或者模式我們就叫做責任鏈模式,當然這個項目其實不一定非定到達到程序員手中再上交,其中某個leader也是可以直接攔截掉這個項目然後完成直接上交給自己的上層,所以有時候我們也把鏈中的每層叫做攔截器,當一個任務傳遞到你這你可以攔截掉也可以繼續向下傳遞下去。
對責任鏈模式有了一個大致的印象後,我們再來看下這個getResponseWithInterceptorChain方法:
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
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);
}
}
}
可以看到,這個方法裏面其實就是執行一個責任鏈,首先是定義了一系列攔截器Interceptor,然後把這些攔截器組成一個鏈條RealInterceptorChain,最後就是執行這個鏈條,這樣我們的一個網絡請求就會在每一層攔截器中傳遞,如下圖所示:
那麼接下來我們的OkHttp分析的重心就放到了各個攔截器的解讀。由於篇幅的關係,關於各個攔截器的分析將會放到下一篇文章中介紹。