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