在《打車APP實(shí)戰(zhàn)》課程中乖仇,我們使用 OkHttp3 簡(jiǎn)單搭建了一個(gè)網(wǎng)絡(luò)框架廉油, 實(shí)踐了 OkHttp3 的用法势誊。不過(guò)課程本身的重點(diǎn)是 MVP 架構(gòu)的實(shí)踐剑梳,所以沒(méi)有進(jìn)一步 OkHttp3 底層的實(shí)現(xiàn)細(xì)節(jié)唆貌。本文來(lái)探究一下 OkHttp3 的源碼和其中的設(shè)計(jì)思想。
關(guān)于 OkHttp3 的源碼分析的文章挺多阻荒,不過(guò)大多還是在為了源碼而源碼挠锥。個(gè)人覺(jué)得如果讀源碼不去分析源碼背后的設(shè)計(jì)模式或設(shè)計(jì)思想众羡,那么讀源碼的意義不大侨赡。 同時(shí),如果熟悉的設(shè)計(jì)模式越多粱侣,那么讀某個(gè)框架的源碼的時(shí)候就越容易羊壹,兩者是相輔相成的,這也是許多大牛認(rèn)為多讀源碼能提高編程能力的原因齐婴。
整體架構(gòu)
為了方面后面的理解油猫,我這里簡(jiǎn)單畫了個(gè)架構(gòu)圖,圖中畫出了 OkHttp3 核心的功能模塊柠偶。為了方便整體理解情妖,這里分了三個(gè)層次: 客戶層睬关、執(zhí)行層和連接層。
首先毡证,客戶層的OkHttpClient 电爹,使用過(guò) OkHttp 網(wǎng)絡(luò)庫(kù)的同學(xué)應(yīng)該都熟悉,在發(fā)送網(wǎng)絡(luò)請(qǐng)求料睛,執(zhí)行層決定怎么處理請(qǐng)求丐箩,比如同步還是異步,同步請(qǐng)求的話直接在當(dāng)前線程完成請(qǐng)求恤煞, 請(qǐng)求要經(jīng)過(guò)多層攔截器處理屎勘; 如果是異步處理,需要 Dispatcher 執(zhí)行分發(fā)策略居扒, 線程池管理執(zhí)行任務(wù)概漱; 又比如,一個(gè)請(qǐng)求下來(lái)苔货,要不要走緩存犀概,如果不走緩存,進(jìn)行網(wǎng)絡(luò)請(qǐng)求夜惭。最后執(zhí)行層將從連接層進(jìn)行網(wǎng)絡(luò) IO 獲取數(shù)據(jù)姻灶。
OkHttpClient
使用過(guò) OkHttp 網(wǎng)絡(luò)庫(kù)的同學(xué)應(yīng)該都熟悉 OkHttpClient , 許多第三方框架都會(huì)提供一個(gè)類似的類作為客戶訪問(wèn)的一個(gè)入口诈茧。 關(guān)于 OkHttpClient 代碼注釋上就說(shuō)的很清楚:
/**
* Factory for {@linkplain Call calls}, which can be used to send
HTTP requests and read their
* responses.
*
* <h3>OkHttpClients should be shared</h3>
*
* <p>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.
*
* <p>Use {@code new OkHttpClient()} to create a shared instance
with the default settings:
* <pre> {@code
*
* // The singleton HTTP client.
* public final OkHttpClient client = new OkHttpClient();
* }</pre>
*
* <p>Or use {@code new OkHttpClient.Builder()} to create a shared
instance with custom settings:
* <pre> {@code
*
* // The singleton HTTP client.
* public final OkHttpClient client = new OkHttpClient.Builder()
* .addInterceptor(new HttpLoggingInterceptor())
* .cache(new Cache(cacheDir, cacheSize))
* .build();
* }</pre>
*
.... 省略
*/
簡(jiǎn)單提煉:
1产喉、OkHttpClient, 可以通過(guò) new OkHttpClient() 或 new OkHttpClient.Builder() 來(lái)創(chuàng)建對(duì)象敢会, 但是---特別注意曾沈, OkHttpClient() 對(duì)象最好是共享的, 建議使用單例模式創(chuàng)建鸥昏。 因?yàn)槊總€(gè) OkHttpClient 對(duì)象都管理自己獨(dú)有的線程池和連接池塞俱。 這一點(diǎn)很多同學(xué),甚至在我經(jīng)歷的團(tuán)隊(duì)中就有人踩過(guò)坑吏垮, 每一個(gè)請(qǐng)求都創(chuàng)建一個(gè) OkHttpClient 導(dǎo)致內(nèi)存爆掉障涯。
2、 從上面的整體框架圖膳汪,其實(shí)執(zhí)行層有很多屬性功能是需要OkHttpClient 來(lái)制定唯蝶,例如緩存、線程池遗嗽、攔截器等粘我。如果你是設(shè)計(jì)者你會(huì)怎樣設(shè)計(jì) OkHttpClient ? 建造者模式痹换,OkHttpClient 比較復(fù)雜征字, 太多屬性都弹, 而且客戶的組合需求多樣化, 這種情況下就考慮使用建造者模式匙姜。 new OkHttpClien() 創(chuàng)建對(duì)象缔杉, 內(nèi)部默認(rèn)指定了很多屬性:
public OkHttpClient() {
this(new Builder());
}
在看看 new Builder() 的默認(rèn)實(shí)現(xiàn):
public Builder() {
dispatcher = new Dispatcher();
protocols = DEFAULT_PROTOCOLS;
connectionSpecs = DEFAULT_CONNECTION_SPECS;
eventListenerFactory = EventListener.factory(EventListener.NONE);
proxySelector = ProxySelector.getDefault();
cookieJar = CookieJar.NO_COOKIES;
socketFactory = SocketFactory.getDefault();
hostnameVerifier = OkHostnameVerifier.INSTANCE;
certificatePinner = CertificatePinner.DEFAULT;
proxyAuthenticator = Authenticator.NONE;
authenticator = Authenticator.NONE;
connectionPool = new ConnectionPool();
dns = Dns.SYSTEM;
followSslRedirects = true;
followRedirects = true;
retryOnConnectionFailure = true;
connectTimeout = 10_000;
readTimeout = 10_000;
writeTimeout = 10_000;
pingInterval = 0;
}
默認(rèn)指定 Dispatcher (管理線程池)、鏈接池搁料、超時(shí)時(shí)間等或详。
3、 內(nèi)部對(duì)于線程池郭计、鏈接池管理有默認(rèn)的管理策略霸琴,例如空閑時(shí)候的線程池、連接池會(huì)在一定時(shí)間自動(dòng)釋放昭伸,但如果你想主動(dòng)去釋放也可以通過(guò)客戶層去釋放梧乘。(很少)
執(zhí)行層
Response response = mOkHttpClient.newCall(request).execute();
這是應(yīng)用程序中發(fā)起網(wǎng)絡(luò)請(qǐng)求最頂端的調(diào)用,newCall(request) 方法返回 RealCall 對(duì)象庐杨。RealCall 封裝了一個(gè) request 代表一個(gè)請(qǐng)求調(diào)用任務(wù)选调,RealCall 有兩個(gè)重要的方法 execute() 和 enqueue(Callback responseCallback)。 execute() 是直接在當(dāng)前線程執(zhí)行請(qǐng)求灵份,enqueue(Callback responseCallback) 是將當(dāng)前任務(wù)加到任務(wù)隊(duì)列中仁堪,執(zhí)行異步請(qǐng)求。
同步請(qǐng)求
@Override public Response execute() throws IOException {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
try {
// client.dispatcher().executed(this) 內(nèi)部只是記錄下執(zhí)行狀態(tài)填渠,
client.dispatcher().executed(this);
// 真正執(zhí)行發(fā)生在這里
Response result = getResponseWithInterceptorChain();
if (result == null) throw new IOException("Canceled");
return result;
} finally {
// 后面再解釋
client.dispatcher().finished(this);
}
}
執(zhí)行方法關(guān)鍵在 getResponseWithInterceptorChain() 這個(gè)方法中弦聂, 關(guān)于 client.dispatcher().executed(this) 和 client.dispatcher().finished(this); 這里先忽略 ,后面再看氛什。
請(qǐng)求過(guò)程要從執(zhí)行層說(shuō)到連接層莺葫,涉及到 getResponseWithInterceptorChain 方法中組織的各個(gè)攔截器的執(zhí)行過(guò)程,內(nèi)容比較多枪眉,后面章節(jié)在說(shuō)捺檬。先說(shuō)說(shuō) RealCall 中 enqueue(Callback responseCallback) 方法涉及的異步請(qǐng)求和線程池。
Dispatcher 和線程池
@Override public void enqueue(Callback responseCallback) {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
調(diào)用了 dispatcher 的 enqueue()方法
dispatcher 結(jié)合線程池完成了所有異步請(qǐng)求任務(wù)的調(diào)配贸铜。
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}
dispatcher 主要維護(hù)了三兩個(gè)隊(duì)列 readyAsyncCalls堡纬、runningAsyncCalls 和 runningSyncCalls,分別代表了準(zhǔn)備中隊(duì)列萨脑, 正在執(zhí)行的異步任務(wù)隊(duì)列和正在執(zhí)行的同步隊(duì)列隐轩, 重點(diǎn)關(guān)注下前面兩個(gè)饺饭。
現(xiàn)在我們可以回頭來(lái)看看前面 RealCall 方法 client.dispatcher().finished(this) 這個(gè)疑點(diǎn)了渤早。 在每個(gè)任務(wù)執(zhí)行完之后要回調(diào) client.dispatcher().finished(this) 方法, 主要是要將當(dāng)前任務(wù)從 runningAsyncCalls 或 runningSyncCalls 中移除瘫俊, 同時(shí)把 readyAsyncCalls 的任務(wù)調(diào)度到 runningAsyncCalls 中并執(zhí)行鹊杖。
線程池
public synchronized ExecutorService executorService() {
if (executorService == null) {
executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));
}
return executorService;
}
默認(rèn)實(shí)現(xiàn)是一個(gè)不限容量的線程池 悴灵, 線程空閑時(shí)存活時(shí)間為 60 秒。線程池實(shí)現(xiàn)了對(duì)象復(fù)用骂蓖,降低線程創(chuàng)建開(kāi)銷积瞒,從設(shè)計(jì)模式上來(lái)講,使用了享元模式登下。
責(zé)任鏈 (攔截器執(zhí)行過(guò)程)
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List<Interceptor> interceptors = new ArrayList<>();
interceptors.addAll(client.interceptors());
interceptors.add(retryAndFollowUpInterceptor);
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, null, null, null, 0, originalRequest);
return chain.proceed(originalRequest);
}
}
要跟蹤 Okhttp3 的網(wǎng)絡(luò)請(qǐng)求任務(wù)執(zhí)行過(guò)程 茫孔,需要看懂以上代碼,看懂以上代碼必須理解設(shè)計(jì)模式-責(zé)任鏈被芳。在責(zé)任鏈模式里缰贝,很多對(duì)象由每一個(gè)對(duì)象對(duì)其下家的引用而連接起來(lái)形成一條鏈。請(qǐng)求在這個(gè)鏈上傳遞畔濒,直到鏈上的某一個(gè)對(duì)象決定處理此請(qǐng)求剩晴。發(fā)出這個(gè)請(qǐng)求的客戶端并不知道鏈上的哪一個(gè)對(duì)象最終處理這個(gè)請(qǐng)求,這使得系統(tǒng)可以在不影響客戶端的情況下動(dòng)態(tài)地重新組織和分配責(zé)任侵状。 網(wǎng)絡(luò)請(qǐng)求過(guò)程赞弥,是比較典型的復(fù)合責(zé)任鏈的場(chǎng)景,比如請(qǐng)求傳遞過(guò)程趣兄,我們需要做請(qǐng)求重試绽左, 需要執(zhí)行緩存策略, 需要建立連接等艇潭, 每一個(gè)處理節(jié)點(diǎn)可以由一個(gè)鏈上的對(duì)象來(lái)處理妇菱; 同時(shí)客戶端使用的時(shí)候可能也會(huì)在請(qǐng)求過(guò)程中做一些應(yīng)用層需要的事情,比如我要記錄網(wǎng)絡(luò)請(qǐng)求的耗時(shí)暴区、日志等闯团, 責(zé)任鏈還可以動(dòng)態(tài)的擴(kuò)展到客戶業(yè)務(wù)方。
在 OkHttp3 的攔截器鏈中仙粱, 內(nèi)置了5個(gè)默認(rèn)的攔截器房交,分別用于重試、請(qǐng)求對(duì)象轉(zhuǎn)換伐割、緩存候味、鏈接、網(wǎng)絡(luò)讀寫隔心。
以上方法中先是添加了客戶端自定義的連接器白群,然后在分別添加內(nèi)置攔截器。
Okhttp3 攔截器類圖
現(xiàn)在我們把對(duì) OkHttp 網(wǎng)絡(luò)請(qǐng)求執(zhí)行過(guò)程的研究轉(zhuǎn)化對(duì)每個(gè)攔截器處理的研究硬霍。
retryAndFollowUpInterceptor 重試機(jī)制
retryAndFollowUpInterceptor 處于內(nèi)置攔截器鏈的最頂端帜慢,在一個(gè)循環(huán)中執(zhí)行重試過(guò)程:
1、首先下游攔截器在處理網(wǎng)絡(luò)請(qǐng)求過(guò)程如拋出異常,則通過(guò)一定的機(jī)制判斷一下當(dāng)前鏈接是否可恢復(fù)的(例如粱玲,異常是不是致命的躬柬、有沒(méi)有更多的線路可以嘗試等),如果可恢復(fù)則重試抽减,否則跳出循環(huán)允青。
2、 如果沒(méi)什么異常則校驗(yàn)下返回狀態(tài)卵沉、代理鑒權(quán)颠锉、重定向等,如果需要重定向則繼續(xù)史汗,否則直接跳出循環(huán)返回結(jié)果木柬。
3、 如果重定向淹办,則要判斷下是否已經(jīng)達(dá)到最大可重定向次數(shù)眉枕, 達(dá)到則拋出異常,跳出循環(huán)怜森。
@Override public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
// 創(chuàng)建連接池管理對(duì)象
streamAllocation = new StreamAllocation(
client.connectionPool(), createAddress(request.url()), callStackTrace);
int followUpCount = 0;
Response priorResponse = null;
while (true) {
if (canceled) {
streamAllocation.release();
throw new IOException("Canceled");
}
Response response = null;
boolean releaseConnection = true;
try {
// 將請(qǐng)求處理傳遞下游攔截器處理
response = ((RealInterceptorChain) chain).proceed(request, streamAllocation, null, null);
releaseConnection = false;
} catch (RouteException e) {
// The attempt to connect via a route failed. The request will not have been sent.
// 線路異常速挑,判斷滿足可恢復(fù)條件,滿足則繼續(xù)循環(huán)重試
if (!recover(e.getLastConnectException(), false, request)) {
throw e.getLastConnectException();
}
releaseConnection = false;
continue;
} catch (IOException e) {
// An attempt to communicate with a server failed. The request may have been sent.
// IO異常副硅,判斷滿足可恢復(fù)條件姥宝,滿足則繼續(xù)循環(huán)重試
boolean requestSendStarted = !(e instanceof ConnectionShutdownException);
if (!recover(e, requestSendStarted, request)) throw e;
releaseConnection = false;
continue;
} finally {
// We're throwing an unchecked exception. Release any resources.
if (releaseConnection) {
streamAllocation.streamFailed(null);
streamAllocation.release();
}
}
// Attach the prior response if it exists. Such responses never have a body.
if (priorResponse != null) {
response = response.newBuilder()
.priorResponse(priorResponse.newBuilder()
.body(null)
.build())
.build();
}
// 是否需要重定向
Request followUp = followUpRequest(response);
if (followUp == null) {
if (!forWebSocket) {
streamAllocation.release();
}
// 不需要重定向,正常返回結(jié)果
return response;
}
closeQuietly(response.body());
if (++followUpCount > MAX_FOLLOW_UPS) {
// 達(dá)到次數(shù)限制
streamAllocation.release();
throw new ProtocolException("Too many follow-up requests: " + followUpCount);
}
if (followUp.body() instanceof UnrepeatableRequestBody) {
streamAllocation.release();
throw new HttpRetryException("Cannot retry streamed HTTP body", response.code());
}
if (!sameConnection(response, followUp.url())) {
streamAllocation.release();
streamAllocation = new StreamAllocation(
client.connectionPool(), createAddress(followUp.url()), callStackTrace);
} else if (streamAllocation.codec() != null) {
throw new IllegalStateException("Closing the body of " + response
+ " didn't close its backing stream. Bad interceptor?");
}
request = followUp;
priorResponse = response;
}
}
BridgeInterceptor
/**
* Bridges from application code to network code. First it builds a
network request from a user
* request. Then it proceeds to call the network. Finally it builds a
user response from the network
* response.
*/
這個(gè)攔截器比較簡(jiǎn)單恐疲, 一個(gè)實(shí)現(xiàn)應(yīng)用層和網(wǎng)絡(luò)層直接的數(shù)據(jù)格式編碼的橋腊满。 第一: 把應(yīng)用層客戶端傳過(guò)來(lái)的請(qǐng)求對(duì)象轉(zhuǎn)換為 Http 網(wǎng)絡(luò)協(xié)議所需字段的請(qǐng)求對(duì)象。 第二培己, 把下游網(wǎng)絡(luò)請(qǐng)求結(jié)果轉(zhuǎn)換為應(yīng)用層客戶所需要的響應(yīng)對(duì)象碳蛋。 這個(gè)設(shè)計(jì)思想來(lái)自適配器設(shè)計(jì)模式,大家可以去體會(huì)一下省咨。
CacheInterceptor 數(shù)據(jù)策略(策略模式)
CacheInterceptor 實(shí)現(xiàn)了數(shù)據(jù)的選擇策略肃弟, 來(lái)自網(wǎng)絡(luò)還是來(lái)自本地? 這個(gè)場(chǎng)景也是比較契合策略模式場(chǎng)景零蓉, CacheInterceptor 需要一個(gè)策略提供者提供它一個(gè)策略(錦囊)笤受, CacheInterceptor 根據(jù)這個(gè)策略去選擇走網(wǎng)絡(luò)數(shù)據(jù)還是本地緩存。
緩存的策略過(guò)程:
1敌蜂、 請(qǐng)求頭包含 "If-Modified-Since" 或 "If-None-Match" 暫時(shí)不走緩存
2箩兽、 客戶端通過(guò) cacheControl 指定了無(wú)緩存,不走緩存
3章喉、客戶端通過(guò) cacheControl 指定了緩存汗贫,則看緩存過(guò)期時(shí)間身坐,符合要求走緩存。
4芳绩、 如果走了網(wǎng)絡(luò)請(qǐng)求,響應(yīng)狀態(tài)碼為 304(只有客戶端請(qǐng)求頭包含 "If-Modified-Since" 或 "If-None-Match" 撞反,服務(wù)器數(shù)據(jù)沒(méi)變化的話會(huì)返回304狀態(tài)碼妥色,不會(huì)返回響應(yīng)內(nèi)容), 表示客戶端繼續(xù)用緩存遏片。
@Override public Response intercept(Chain chain) throws IOException {
Response cacheCandidate = cache != null
? cache.get(chain.request())
: null;
long now = System.currentTimeMillis();
CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();
// 獲取緩存策略
Request networkRequest = strategy.networkRequest;
Response cacheResponse = strategy.cacheResponse;
if (cache != null) {
cache.trackResponse(strategy);
}
if (cacheCandidate != null && cacheResponse == null) {
closeQuietly(cacheCandidate.body()); // The cache candidate wasn't applicable. Close it.
}
// If we're forbidden from using the network and the cache is insufficient, fail.
if (networkRequest == null && cacheResponse == null) {
return new Response.Builder()
.request(chain.request())
.protocol(Protocol.HTTP_1_1)
.code(504)
.message("Unsatisfiable Request (only-if-cached)")
.body(Util.EMPTY_RESPONSE)
.sentRequestAtMillis(-1L)
.receivedResponseAtMillis(System.currentTimeMillis())
.build();
}
// 走緩存
if (networkRequest == null) {
return cacheResponse.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.build();
}
Response networkResponse = null;
try {
// 執(zhí)行網(wǎng)絡(luò)
networkResponse = chain.proceed(networkRequest);
} finally {
// If we're crashing on I/O or otherwise, don't leak the cache body.
if (networkResponse == null && cacheCandidate != null) {
closeQuietly(cacheCandidate.body());
}
}
// 返回 304 仍然走本地緩存
if (cacheResponse != null) {
if (networkResponse.code() == HTTP_NOT_MODIFIED) {
Response response = cacheResponse.newBuilder()
.headers(combine(cacheResponse.headers(), networkResponse.headers()))
.sentRequestAtMillis(networkResponse.sentRequestAtMillis())
.receivedResponseAtMillis(networkResponse.receivedResponseAtMillis())
.cacheResponse(stripBody(cacheResponse))
.networkResponse(stripBody(networkResponse))
.build();
networkResponse.body().close();
// Update the cache after combining headers but before stripping the
// Content-Encoding header (as performed by initContentStream()).
cache.trackConditionalCacheHit();
cache.update(cacheResponse, response);
return response;
} else {
closeQuietly(cacheResponse.body());
}
}
Response response = networkResponse.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.networkResponse(stripBody(networkResponse))
.build();
if (cache != null) {
if (HttpHeaders.hasBody(response) && CacheStrategy.isCacheable(response, networkRequest)) {
// 存儲(chǔ)緩存
CacheRequest cacheRequest = cache.put(response);
return cacheWritingResponse(cacheRequest, response);
}
if (HttpMethod.invalidatesCache(networkRequest.method())) {
try {
cache.remove(networkRequest);
} catch (IOException ignored) {
// The cache cannot be written.
}
}
}
return response;
}
緩存實(shí)現(xiàn)
OkHttp3 內(nèi)部緩存默認(rèn)實(shí)現(xiàn)是使用的 DiskLruCache嘹害, 這部分代碼有點(diǎn)繞:
interceptors.add(new CacheInterceptor(client.internalCache()));
初始化 CacheInterceptor 時(shí)候 client.internalCache() 這里獲取OkHttpClient的緩存。
InternalCache internalCache() {
return cache != null ? cache.internalCache : internalCache;
}
注意到吮便, 這個(gè)方法是非公開(kāi)的笔呀。 客戶端只能通過(guò) OkhttpClient.Builder的 cache(cache) 定義緩存, cache 是一個(gè) Cache 對(duì)實(shí)例髓需。 在看看 Cache 的內(nèi)部實(shí)現(xiàn)许师, 內(nèi)部有一個(gè) InternalCache 的內(nèi)部類實(shí)現(xiàn)硫惕。 內(nèi)部調(diào)用時(shí)使用 InternalCache 實(shí)例提供接口,而存儲(chǔ)邏輯在 Cache 中實(shí)現(xiàn)溺忧。
Cache 為什么不直接實(shí)現(xiàn) InternalCache ,而通過(guò)持有 InternalCache 的一個(gè)內(nèi)部類對(duì)象來(lái)實(shí)現(xiàn)方法循狰? 是希望控制緩存實(shí)現(xiàn)咧擂, 不希望用戶外部去實(shí)現(xiàn)緩存逞盆,同時(shí)對(duì)內(nèi)保持一定的擴(kuò)展。
鏈接層
RealCall 封裝了請(qǐng)求過(guò)程松申, 組織了用戶和內(nèi)置攔截器云芦,其中內(nèi)置攔截器 retryAndFollowUpInterceptor -> BridgeInterceptor -> CacheInterceptor 完執(zhí)行層的大部分邏輯 ,ConnectInterceptor -> CallServerInterceptor 兩個(gè)攔截器開(kāi)始邁向連接層最終完成網(wǎng)絡(luò)請(qǐng)求贸桶。 關(guān)于 ConnectInterceptor -> CallServerInterceptor 要結(jié)合連接層一起說(shuō)明舅逸,限于篇幅, 下一篇文章:《OkHttp3源碼和設(shè)計(jì)模式-2 》接著分析皇筛。