Android設計模式之從OKHttp的攔截器中學習責任鏈模式
前言
責任鏈模式,顧名思義,就是由一個個負有一定責任的單元形成的一個鏈條,
在這個鏈條上,每個責任單元都負責自己應該負責的責任,
而責任單元之間時互不干擾的,當有事件需要處理時,從鏈條的
首個責任單元開始處理,首個責任單元處理事件中自己負責的部分,
當處理完之后,若事件還未處理完
畢,還需進一步處理而同時當前責任單元無法處理或是不是自己負責的部分時,
當前責任單元將事件傳
遞給下一個責任單元,而后面該哪個責任單元處理,當前責任單元不關心,
當前責任單元只需處理自己
負責的部分并確定事件是否該繼續(xù)傳遞到下一責任單元。
Android開發(fā)中用到的責任鏈模式
- 相信大家在Android開發(fā)過程中都用到過okhttp或是Retrofit網(wǎng)絡請求框架,而okhttp中就是使用到了責任鏈設計模式,即便使用的時retrofit,而retrofit也只是封裝的okhttp。
- okhttp中的攔截器使用的就是責任鏈設計模式,相信大家都會有用到過這其中的攔截器去處理網(wǎng)絡請求,比如對Cookie的處理。下面將從okhttp攔截器的實現(xiàn)源碼角度學習責任鏈設計模式。
攔截器的使用
如需對cookie進行處理,我們一般會定義一個攔截器類繼承自Interceptor,并重寫intercept方法,
在該方法中處理cookie(添加或獲取cookie保存),
以下代碼實現(xiàn)的是向請求頭加入cookie的攔截器,獲取請求頭中cookie方法與此類似,這里不做展示。
- /**
- * 定義OkHttp3攔截器,處理數(shù)據(jù)包的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方法,參數(shù)就是創(chuàng)建的攔截器類對象,我這里是添加了兩個攔截器,包括cookie的添加和獲取。
- okHttpClient = new OkHttpClient
- .Builder()
- .addInterceptor(new InterceptorOfAddCookie())
- .addInterceptor(new InterceptorOfReceivedCookie())
- .build();
正式進入正題,切入點就在這里的addInterceptor方法,查看一下該方法的源碼,看一下內(nèi)部實現(xiàn)了怎樣的邏輯處理。
- /**
- * 通過addInterceptor方法將自定義的cookie處理攔截器添加到這的interceptor
- * 中,在源碼中可以看到interceptors其實就是一個List集合,即攔截器集合,而這里
- * 的攔截器集合就可以看作是我們這次責任鏈模式中的責任鏈,集合中的每一個攔截器就
- * 相當于之前所說的責任單元。
- */
- public Builder addInterceptor(Interceptor interceptor) {
- if (interceptor == null) throw new IllegalArgumentException("interceptor == null");
- interceptors.add(interceptor);
- return this;
- }
然后是在再看到ohhttpClient中使用攔截器并發(fā)送請求的過程
- 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()方法源碼如下:
- @Override public Call newCall(Request request) {
- return RealCall.newRealCall(this, request, false /* for web socket */);
- }
可以看到newCall方法實際上創(chuàng)建的是RealCall對象,RealCall類實現(xiàn)了Call方法。接著再到call對象調(diào)用enqueue(CallBack callBack)方法發(fā)起請求,進入到enqueue內(nèi)部查看,即進入到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));
- }
可以看到這邊創(chuàng)建了一個AsyncCall對象,并傳入CallBack對象,在RealCall類中可以找到合格內(nèi)部類AsyncCall是繼承自NamedRunnable,而進一步查看NamedRunnable是繼承自
Runnable,所以AsyncCall可以被看作為一個Runnable
沿著client.dispatcher().enqueue(new AsyncCall(responseCallback));方法進入到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();
- }
可以發(fā)現(xiàn)這里最終調(diào)用了promoterAndExecute()方法,再看一下這個方法中具體實現(xiàn)
- 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;
- }
可以發(fā)現(xiàn)在這個方法最終會調(diào)用
- asyncCall.executeOn(executorService());這里的executeOn傳入的參為線程池對象
- ExecutorService實例,在回到AsyncCall類中查看executeOn方法的具體實現(xiàn),
- 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是實現(xiàn)了Runnable的)傳入到線程池中,當線程池執(zhí)行該Runnable任務時回執(zhí)行run()方法,而可以看到AsyncCall父類NameRunnable類中的run()方法是調(diào)用了自身的execute()方法,而在AsyncCall中重寫了該execute()方法,即執(zhí)行NameRunnable的execute()相當于執(zhí)行了AsyncCall類中的execute()方法。
再看到execute()方法中,在這個方法中主要看到Response response = getResponseWithInterceptorChain();這一行,查看一下getResponseWithInterceptorChain()方法的實現(xiàn):
- 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);
- }
- }
- }
這里發(fā)現(xiàn)了創(chuàng)建了一個攔截器集合,并通過client.interceptors()方法獲取到了client的攔截器集合interceptors,隨后也往新創(chuàng)建的攔截器集合添加了其他的攔截器,而client中的攔截器集合包含的只是我們自定義的攔截器集合,還記得起初提到的創(chuàng)建okhttpClient實例時通過addInterceptor方法添加自定義攔截器嗎?所以在這里也可以發(fā)現(xiàn),如果處理攔截器的時候會先執(zhí)行我們自定義的攔截器再執(zhí)行內(nèi)部的攔截器。
再往下看會發(fā)現(xiàn)Interceptor.Chain chain = new RealInterceptorChain()傳入iterceptors創(chuàng)建了Interceptor.Chain,這個就是責任鏈,將攔截器集合都放到這個鏈條上,組成了一個攔截器責任鏈。
注意:RealInterceptorChain實現(xiàn)了Interceptor接口中的內(nèi)部接口Chain接口。
接著往下看Response response = chain.proceed(originalRequest);這里執(zhí)行了chain的proceed方法并傳入了Request對象originalRequest(即是我們最初創(chuàng)建
Call call = okHttpClient.newCall(request),RealCall對象)
接著再看chain.proceed方法的具體實現(xiàn)(進入到RealInterceptorChain類中,因為該類實現(xiàn)了Chain接口,所以具體邏輯實現(xiàn)會在該類的proceed中):
- @Override public Response proceed(Request request) throws IOException {
- return proceed(request, transmitter, exchange);
- }
其內(nèi)部依然調(diào)用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);
這里又通過調(diào)用RealInterceptorChain類構造方法,而這里不同的是,參數(shù)index的值為index+1,并且在該類中index為全局變量,所以index的值增量為1,通過index將攔截器集合interceptors中的第index個攔截器interceptor取出,并執(zhí)行interceptor的interceprt(Chain)方法,接著我們回顧一下最初我們自定義的攔截其中實現(xiàn)了什么邏輯:
- 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)方法中調(diào)用了
chain.proceed(Request)方法,即又調(diào)用了proceed方法,而前面分析到RealInterceptorChain重寫父接口的proceed方法的具體實現(xiàn)中又調(diào)用了RealInterceptorChain自身的proceed方法,而自身的proceed方法又調(diào)用了interceptor.intercept()方法,所以這里是形成了一個遞歸,而這里的遞歸
思想就是責任鏈模式的核心思想。即不斷執(zhí)行攔截器interceptor中的intercept(Chain)方法,而我們只需要在intercept方法中實現(xiàn)我們的邏輯即可,可以通過Chain獲取到Request或者Response,實現(xiàn)對請求體或請求頭的處理,如處理請求頭的cookie。
總結
okhttp中的攔截器實現(xiàn)可以總結為如下:
這樣的設計方法明顯易于后續(xù)擴展,
而不會涉及到期待責任單元的邏輯更改,
只需創(chuàng)建一個類要實現(xiàn)責任單元接口,創(chuàng)建這個類的實例,
并將其添加到責任鏈中即可。該設計模式的關鍵思想在于遞歸,
在責任鏈Chain中通過遞歸調(diào)用責任單元方法,
即可將要處理的事件沿著責任鏈傳遞處理,
也可以在責任單元中通過邏輯判斷是否要將事件繼續(xù)傳遞到下一責任單元。
本文轉(zhuǎn)載自微信公眾號「Android開發(fā)編程 」