設計模式之責任鏈模式(結合OKHttp的攔截器學習)
highlight: a11y-dark
前言
責任鏈模式,顧名思義,就是由一個個負有一定責任的單元形成的一個鏈條,在這個鏈條上,每個
責任單元都負責自己應該負責的責任,而責任單元之間時互不干擾的,當有事件需要處理時,從鏈條的
首個責任單元開始處理,首個責任單元處理事件中自己負責的部分,當處理完之後,若事件還未處理完
畢,還需進一步處理而同時當前責任單元無法處理或是不是自己負責的部分時,當前責任單元將事件傳
遞給下一個責任單元,而後面該哪個責任單元處理,當前責任單元不關心,當前責任單元只需處理自己
負責的部分並確定事件是否該繼續傳遞到下一責任單元。
舉例
Android開發中用到的責任鏈模式
相信大家在Android開發過程中都用到過okhttp或是Retrofit網路請求框架,而okhttp中就是使用到了責任鏈設計模式,即便使用的時retrofit,而retrofit也只是封裝的okhttp。okhttp中的攔截器使用的就是責任鏈設計模式,相信大家都會有用到過這其中的攔截器去處理網路請求,比如對Cookie的處理。下面將從okhttp攔截器的實現原始碼角度學習責任鏈設計模式。
1. 攔截器的使用
如需對cookie進行處理,我們一般會定義一個攔截器類繼承自Interceptor,並重寫intercept方法,在該方法中處理cookie(新增或獲取cookie儲存),以下程式碼實現的是向請求頭加入cookie的攔截器,獲取請求頭中cookie方法與此類似,這裡不做展示。 ````java /* * 定義OkHttp3攔截器,處理資料包的header,向header種新增cookie / public class InterceptorOfAddCookie implements Interceptor {
private static final String TAG = "InterceptorOfAddCookie";
@Override
public Response intercept(Chain chain) throws IOException {
Log.d(TAG, "InterceptorOfAddCookie");
return chain.proceed(chain.request());
}
}
接著向okhttpClient物件中新增攔截器,使用的方法如下面的addInterceptor方法,引數就是建立的攔截器類物件,我這裡是添加了兩個攔截器,包括cookie的新增和獲取。
java
okHttpClient = new OkHttpClient
.Builder()
.addInterceptor(new InterceptorOfAddCookie())
.addInterceptor(new InterceptorOfReceivedCookie())
.build();
正式進入正題,切入點就在這裡的addInterceptor方法,檢視一下該方法的原始碼,看一下內部實現了怎樣的邏輯處理。
java
/*
* 通過addInterceptor方法將自定義的cookie處理攔截器新增到這的interceptor
* 中,在原始碼中可以看到interceptors其實就是一個List集合,即攔截器集合,而這裡
* 的攔截器集合就可以看作是我們這次責任鏈模式中的責任鏈,集合中的每一個攔截器就
* 相當於之前所說的責任單元。
/
public Builder addInterceptor(Interceptor interceptor) {
if (interceptor == null) throw new IllegalArgumentException("interceptor == null");
interceptors.add(interceptor);
return this;
}
然後是在再看到ohhttpClient中使用攔截器併發送請求的過程
java
okHttpClient = new OkHttpClient
.Builder()
.addInterceptor(new InterceptorOfAddCookie())
.addInterceptor(new InterceptorOfReceivedCookie())
.build();
Request request = new Request.Builder().url("").get().build(); Call call = okHttpClient.newCall(request); call.enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
}
});
其中攔截器是被新增到了okhttpClient的攔截器集合interceptors中,而通過okHttpClient.newCall(request)方法將okhttpClient引用到了RealCall中的client,
因為在okHttpClient.newCall()方法原始碼如下:
java
@Override public Call newCall(Request request) {
return RealCall.newRealCall(this, request, false / for web socket /);
}
可以看到newCall方法實際上建立的是RealCall物件,RealCall類實現了Call方法。接著再到call物件呼叫enqueue(CallBack callBack)方法發起請求,進入到enqueue內部檢視,即進入到RealCall中的enqueue()方法中:
java
@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));
}
可以看到這邊建立了一個AsyncCall物件,並傳入CallBack物件,在RealCall類中可以找到合格內部類AsyncCall是繼承自NamedRunnable,而進一步檢視NamedRunnable是繼承自
Runnable,所以AsyncCall可以被看作為一個Runnable
沿著client.dispatcher().enqueue(new AsyncCall(responseCallback));方法進入到Dispatcher類中的enqueue方法中,
java
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(); }
可以發現這裡最終呼叫了promoterAndExecute()方法,再看一下這個方法中具體實現
private boolean promoteAndExecute() { assert (!Thread.holdsLock(this));
List
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; } ````
可以發現在這個方法最終會呼叫 asyncCall.executeOn(executorService());這裡的executeOn傳入的參為執行緒池物件 ExecutorService例項,在回到AsyncCall類中檢視executeOn方法的具體實現, ````java 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! } } }
可以看到executorService.execute(this);就是將this(即AsyCall物件,而AsyncCall之前提到它
的父類NameRunnable是實現了Runnable的)傳入到執行緒池中,當執行緒池執行該Runnable任務時回執行
run()方法,而可以看到AsyncCall父類NameRunnable類中的run()方法是呼叫了自身的execute()方
法,而在AsyncCall中重寫了該execute()方法,即執行NameRunnable的execute()相當於執行了AsyncCall類中的execute()方法。
再看到execute()方法中,在這個方法中主要看到Response response = getResponseWithInterceptorChain();這一行,檢視一下getResponseWithInterceptorChain()方法的實現:
java
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List
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()方法獲取到了client的攔截器集 合interceptors,隨後也往新建立的攔截器集合添加了其他的攔截器,而client中的攔截器集合包含 的只是我們自定義的攔截器集合,還記得起初提到的建立okhttpClient例項時通過addInterceptor 方法新增自定義攔截器嗎?所以在這裡也可以發現,如果處理攔截器的時候會先執行我們自定義的攔截 器再執行內部的攔截器。
再往下看會發現Interceptor.Chain chain = new RealInterceptorChain()傳入iterceptors建立 了Interceptor.Chain,這個就是責任鏈,將攔截器集合都放到這個鏈條上,組成了一個攔截器責任鏈。 注意:RealInterceptorChain實現了Interceptor介面中的內部介面Chain介面。
接著往下看Response response = chain.proceed(originalRequest);這裡執行了chain的proceed方法並傳入了Request物件originalRequest(即是我們最初建立
Call call = okHttpClient.newCall(request),RealCall物件)
接著再看chain.proceed方法的具體實現(進入到RealInterceptorChain類中,因為該類實現了Chain介面,所以具體邏輯實現會在該類的proceed中):
java
@Override public Response proceed(Request request) throws IOException {
return proceed(request, transmitter, exchange);
}
其內部依然呼叫proceed方法,再看自身的proceed方法: 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 next = new RealInterceptorChain(interceptors, transmitter, exchange,index + 1, request, call, connectTimeout, readTimeout, writeTimeout); Interceptor interceptor = interceptors.get(index); Response response = interceptor.intercept(next);
這裡又通過呼叫RealInterceptorChain類構造方法,而這裡不同的是,引數index的值為index+1,並且在該類中index為全域性變數,所以index的值增量為1,通過index將攔截器集合interceptors中的第index個攔截器interceptor取出,並執行interceptor的interceprt(Chain)方法,接著我們回顧一下最初我們自定義的攔截其中實現了什麼邏輯:
public class InterceptorOfAddCookie implements Interceptor {
private static final String TAG = "InterceptorOfAddCookie";
@Override
public Response intercept(Chain chain) throws IOException {
Log.d(TAG, "InterceptorOfAddCookie");
return chain.proceed(chain.request());
}
}
可以看到intercept(Chain)(這裡的Chain為RealInterceptorChain)方法中呼叫了 chain.proceed(Request)方法,即又呼叫了proceed方法,而前面分析到RealInterceptorChain重 寫父介面的proceed方法的具體實現中又呼叫了RealInterceptorChain自身的proceed方法,而自身 的proceed方法又呼叫了interceptor.intercept()方法,所以這裡是形成了一個遞迴,而這裡的遞迴 思想就是責任鏈模式的核心思想。即不斷執行攔截器interceptor中的intercept(Chain)方法,而我 們只需要在intercept方法中實現我們的邏輯即可,可以通過Chain獲取到Request或者Response,實現 對請求體或請求頭的處理,如處理請求頭的cookie。 ````
總結
okhttp中的攔截器實現可以總結為如下: ````java 責任單元(抽取為介面方便擴充套件) public interface Interceptor { Response intercept(Chain chain) throws IOException; }
責任鏈 public final class Chain {
private final List
public RealInterceptorChain(List
@Override public Response proceed(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();
RealInterceptorChain next = new RealInterceptorChain(interceptors,transmitter, exchange,
index + 1, request, call, connectTimeout, readTimeout, writeTimeout);
//注意這裡的index+1
Interceptor interceptor = interceptors.get(index);
interceptor.intercept(Chain);//這裡的Chain通過構造方法建立
return response;
} }
責任單元邏輯程式碼實現 public class InterceptorOfAddCookie implements Interceptor {
private static final String TAG = "InterceptorOfAddCookie";
@Override
public Response intercept(Chain chain) throws IOException {
/**
*這邊實現具體的邏輯處理
*/
return chain.proceed(Request);
//呼叫責任鏈中的proceed方法實現遞迴呼叫
//這裡的request通過在Chain中定義獲取方法得到request物件
}
}
```` 這樣的設計方法明顯易於後續擴充套件,而不會涉及到期待責任單元的邏輯更改,只需建立一個類要實現責任單元介面,建立這個類的例項,並將其新增到責任鏈中即可。該設計模式的關鍵思想在於遞迴,在責任鏈Chain中通過遞迴呼叫責任單元方法,即可將要處理的事件沿著責任鏈傳遞處理,也可以在責任單元中通過邏輯判斷是否要將事件繼續傳遞到下一責任單元。