參考
徹底理解OkHttp - OkHttp 源碼解析及OkHttp的設(shè)計思想
Okhttp3源碼分析
OkHttpClient okHttpClient = new OkHttpClient();
OkHttpClient.Builder builder = okHttpClient.newBuilder();
builder.addInterceptor(new HttpLoggingInterceptor());
Request request = new Request.Builder()
.url(url)
.get()
.build()
;
okHttpClient.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
System.out.println(e.toString());
}
@Override
public void onResponse(Call call, Response response) throws IOException {
System.out.println(response.body().toString());
}
});
final class RealCall implements Call {
@Override public void enqueue(Callback responseCallback) {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
eventListener.callStart(this);
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
}
public final class Dispatcher {
//TODO 同時能進行的最大請求數(shù)
private int maxRequests = 64;
//TODO 同時請求的相同HOST的最大個數(shù) SCHEME :// HOST [ ":" PORT ] [ PATH [ "?" QUERY ]]
//TODO 如 https://restapi.amap.com restapi.amap.com - host
private int maxRequestsPerHost = 5;
/** Ready async calls in the order they'll be run. */
/**
* Ready async calls in the order they'll be run.
* TODO 雙端隊列,支持首尾兩端 雙向開口可進可出觅够,方便移除
* 異步等待隊列
*
*/
private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
/** Running asynchronous calls. Includes canceled calls that haven't finished yet. */
//正在進行的異步隊列
private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
//正在進行的同步隊列
/** Running synchronous calls. Includes canceled calls that haven't finished yet. */
private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
synchronized void enqueue(AsyncCall call) {
// //TODO 同時請求不能超過并發(fā)數(shù)(64,可配置調(diào)度器調(diào)整)
//TODO okhttp會使用共享主機即 地址相同的會共享socket
//TODO 同一個host最多允許5條線程通知執(zhí)行請求
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
// //TODO 加入運行隊列 并交給線程池執(zhí)行
runningAsyncCalls.add(call);
//TODO AsyncCall 是一個runnable跪呈,放到線程池中去執(zhí)行碉输,查看其execute實現(xiàn)
executorService().execute(call);
} else {
//TODO 加入等候隊列
readyAsyncCalls.add(call);
}
}
}
//TODO 沒有核心線程 秫筏,非核心線程數(shù)量沒有限制隘冲,閑置60秒回收 任務(wù)隊列抵赢,
//這個線程池跟Android中的CachedThreadPool非常類似欺劳,這種類型的線程池唧取,
//適用于大量的耗時較短的異步任務(wù)
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;
}
call放到了線程池中那么它是如何執(zhí)行的呢?注意這里的call是AsyncCall划提。
我們看一下AsyncCall的實現(xiàn):
final class RealCall implements Call {
//public abstract class NamedRunnable implements Runnable
final class AsyncCall extends NamedRunnable {
private final Callback responseCallback;
AsyncCall(Callback responseCallback) {
super("OkHttp %s", redactedUrl());
this.responseCallback = responseCallback;
}
//線程池實際上就是執(zhí)行了execute()
@Override protected void execute() {
boolean signalledCallback = false;
try {
//
Response response = getResponseWithInterceptorChain();
if (retryAndFollowUpInterceptor.isCanceled()) {
signalledCallback = true;
responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
} else {
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 {
eventListener.callFailed(RealCall.this, e);
responseCallback.onFailure(RealCall.this, e);
}
} finally {
//TODO 移除隊列
client.dispatcher().finished(this);
}
}
}
}
值得注意的finally 執(zhí)行了client.dispatcher().finished(this); 通過調(diào)度器移除隊列枫弟,并且判斷是否存在等待隊列,如果存在鹏往,檢查執(zhí)行隊列是否達到最大值淡诗,如果沒有將等待隊列變?yōu)閳?zhí)行隊列。這樣也就確保了等待隊列被執(zhí)行伊履。
public final class Dispatcher {
/** Used by {@code AsyncCall#run} to signal completion. */
void finished(AsyncCall call) {
finished(runningAsyncCalls, call, true);
}
private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {
int runningCallsCount;
Runnable idleCallback;
synchronized (this) {
if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
//promoteCalls=true,即異步調(diào)用
if (promoteCalls) promoteCalls();
//TODO 運行隊列的數(shù)量
runningCallsCount = runningCallsCount();
idleCallback = this.idleCallback;
}
//閑置調(diào)用
if (runningCallsCount == 0 && idleCallback != null) {
idleCallback.run();
}
}
private void promoteCalls() {
//正在執(zhí)行的異步隊列大于最大請求隊列 韩容,不執(zhí)行下面代碼
if (runningAsyncCalls.size() >= maxRequests) return; // Already running max capacity.
//等待隊列為空,不執(zhí)行下面代碼
if (readyAsyncCalls.isEmpty()) return; // No ready calls to promote.
for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
AsyncCall call = i.next();
// //TODO 相同host的請求沒有達到最大唐瀑,加入運行隊列
if (runningCallsForHost(call) < maxRequestsPerHost) {
//將等待隊列加入到運行隊列中
i.remove();
runningAsyncCalls.add(call);
//交給線程池來執(zhí)行
executorService().execute(call);
}
//當(dāng)runningAsyncCalls滿了群凶,直接退出迭代
if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity.
}
}
}
真正的執(zhí)行網(wǎng)絡(luò)請求和返回響應(yīng)結(jié)果:getResponseWithInterceptorChain(),下面我們著重分析一下這個方法:
final class RealCall implements Call {
//TODO 核心代碼 開始真正的執(zhí)行網(wǎng)絡(luò)請求
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
//TODO 責(zé)任鏈
List<Interceptor> interceptors = new ArrayList<>();
//TODO 在配置okhttpClient 時設(shè)置的intercept 由用戶自己設(shè)置
////首先添加的是用戶添加的全局?jǐn)r截器
interceptors.addAll(client.interceptors());
//TODO 負(fù)責(zé)處理失敗后的重試與重定向
interceptors.add(retryAndFollowUpInterceptor);
//TODO 負(fù)責(zé)把用戶構(gòu)造的請求轉(zhuǎn)換為發(fā)送到服務(wù)器的請求 哄辣、把服務(wù)器返回的響應(yīng)轉(zhuǎn)換為用戶友好的響應(yīng) 處理 配置請求頭等信息
//TODO 從應(yīng)用程序代碼到網(wǎng)絡(luò)代碼的橋梁座掘。首先,它根據(jù)用戶請求構(gòu)建網(wǎng)絡(luò)請求柔滔。然后它繼續(xù)呼叫網(wǎng)絡(luò)。最后萍虽,它根據(jù)網(wǎng)絡(luò)響應(yīng)構(gòu)建用戶響應(yīng)睛廊。
interceptors.add(new BridgeInterceptor(client.cookieJar()));
//TODO 處理 緩存配置 根據(jù)條件(存在響應(yīng)緩存并被設(shè)置為不變的或者響應(yīng)在有效期內(nèi))返回緩存響應(yīng)
//TODO 設(shè)置請求頭(If-None-Match、If-Modified-Since等) 服務(wù)器可能返回304(未修改)
//TODO 可配置用戶自己設(shè)置的緩存攔截器
interceptors.add(new CacheInterceptor(client.internalCache()));
//TODO 連接服務(wù)器 負(fù)責(zé)和服務(wù)器建立連接 這里才是真正的請求網(wǎng)絡(luò)
interceptors.add(new ConnectInterceptor(client));
if (!forWebSocket) {
//TODO 配置okhttpClient 時設(shè)置的networkInterceptors
//TODO 返回觀察單個網(wǎng)絡(luò)請求和響應(yīng)的不可變攔截器列表杉编。
interceptors.addAll(client.networkInterceptors());
}
//TODO 執(zhí)行流操作(寫出請求體超全、獲得響應(yīng)數(shù)據(jù)) 負(fù)責(zé)向服務(wù)器發(fā)送請求數(shù)據(jù)、從服務(wù)器讀取響應(yīng)數(shù)據(jù)
//TODO 進行http請求報文的封裝與請求報文的解析
interceptors.add(new CallServerInterceptor(forWebSocket));
//TODO 創(chuàng)建責(zé)任鏈
Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
originalRequest, this, eventListener, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());
//TODO 執(zhí)行責(zé)任鏈
return chain.proceed(originalRequest);
}
}
從上述代碼中邓馒,可以看出都實現(xiàn)了Interceptor接口嘶朱,這是okhttp最核心的部分,采用責(zé)任鏈的模式來使每個功能分開光酣,每個Interceptor自行完成自己的任務(wù)疏遏,并且將不屬于自己的任務(wù)交給下一個,簡化了各自的責(zé)任和邏輯救军。
責(zé)任鏈模式是設(shè)計模式中的一種也相當(dāng)簡單參考鏈接财异,這里不在復(fù)述。
我們著重分析一下唱遭,okhttp的設(shè)計實現(xiàn)戳寸,如何通過責(zé)任鏈來進行傳遞返回數(shù)據(jù)的。
上述代碼中可以看出interceptors拷泽,是傳遞到了RealInterceptorChain該類實現(xiàn)了Interceptor.Chain疫鹊,并且執(zhí)行了chain.proceed(originalRequest)袖瞻。
其實核心代碼就是chain.proceed() 通過該方法進行責(zé)任鏈的執(zhí)行
public final class RealInterceptorChain implements Interceptor.Chain {
@Override public Response proceed(Request request) throws IOException {
return proceed(request, streamAllocation, httpCodec, connection);
}
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
RealConnection connection) throws IOException {
if (index >= interceptors.size()) throw new AssertionError();
calls++;
// Call the next interceptor in the chain.
RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
writeTimeout);
Interceptor interceptor = interceptors.get(index);
Response response = interceptor.intercept(next);
return response;
}
}
從上述代碼,我們可以知道拆吆,新建了一個RealInterceptorChain 責(zé)任鏈 并且 index+1聋迎,然后 執(zhí)行interceptors.get(index); 返回Response。
其實就是按順序執(zhí)行了攔截器锈拨,這里我畫了一個簡圖:
攔截器的執(zhí)行順序便是如上圖這樣執(zhí)行的砌庄。
這樣設(shè)計的一個好處就是,責(zé)任鏈中每個攔截器都會執(zhí)行chain.proceed()方法之前的代碼奕枢,等責(zé)任鏈最后一個攔截器執(zhí)行完畢后會返回最終的響應(yīng)數(shù)據(jù)娄昆,而chain.proceed() 方法會得到最終的響應(yīng)數(shù)據(jù),這時就會執(zhí)行每個攔截器的chain.proceed()方法之后的代碼缝彬,其實就是對響應(yīng)數(shù)據(jù)的一些操作萌焰。
CacheInterceptor 緩存攔截器就是很好的證明,我們來通過CacheInterceptor 緩存攔截器來進行分析谷浅,大家就會明白了扒俯。
/** Serves requests from the cache and writes responses to the cache. */
public final class CacheInterceptor implements Interceptor {
@Override public Response intercept(Chain chain) throws IOException {
//TODO 獲取request對應(yīng)緩存的Response 如果用戶沒有配置緩存攔截器 cacheCandidate == null
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;
//TODO 獲取緩存中(CacheStrategy)的Response
Response cacheResponse = strategy.cacheResponse;
if (cache != null) {
cache.trackResponse(strategy);
}
//TODO 緩存無效 關(guān)閉資源
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.
//TODO networkRequest == null 不實用網(wǎng)路請求 且沒有緩存 cacheResponse == null 返回失敗
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();
}
//TODO 不使用網(wǎng)絡(luò)請求 且存在緩存 直接返回響應(yīng)
// If we don't need the network, we're done.
if (networkRequest == null) {
return cacheResponse.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.build();
}
}
上述的代碼,主要做了幾件事:
如果用戶自己配置了緩存攔截器一疯,cacheCandidate = cache.Response 獲取用戶自己存儲的Response,否則 cacheCandidate = null;同時從CacheStrategy 獲取cacheResponse 和 networkRequest
如果cacheCandidate 撼玄!= null 而 cacheResponse == null 說明緩存無效清楚cacheCandidate緩存陨闹。
如果networkRequest == null 說明沒有網(wǎng)絡(luò)鳍征,cacheResponse == null 沒有緩存,返回失敗的信息融虽,責(zé)任鏈此時也就終止眉睹,不會在往下繼續(xù)執(zhí)行荔茬。
如果networkRequest == null 說明沒有網(wǎng)絡(luò),cacheResponse != null 有緩存竹海,返回緩存的信息慕蔚,責(zé)任鏈此時也就終止,不會在往下繼續(xù)執(zhí)行斋配。
上部分代碼孔飒,其實就是沒有網(wǎng)絡(luò)的時候的處理。
/** Serves requests from the cache and writes responses to the cache. */
public final class CacheInterceptor implements Interceptor {
//TODO 執(zhí)行下一個攔截器
Response networkResponse = null;
try {
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());
}
}
//TODO 網(wǎng)絡(luò)請求 回來 更新緩存
// If we have a cache response too, then we're doing a conditional get.
//TODO 如果存在緩存 更新
if (cacheResponse != null) {
//TODO 304響應(yīng)碼 自從上次請求后许起,請求需要響應(yīng)的內(nèi)容未發(fā)生改變
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());
}
}
//TODO 緩存Response
Response response = networkResponse.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.networkResponse(stripBody(networkResponse))
.build();
if (cache != null) {
if (HttpHeaders.hasBody(response) && CacheStrategy.isCacheable(response, networkRequest)) {
// Offer this request to the cache.
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;
}
下部分代碼主要做了這幾件事:
執(zhí)行下一個攔截器十偶,也就是請求網(wǎng)絡(luò)
責(zé)任鏈執(zhí)行完畢后,會返回最終響應(yīng)數(shù)據(jù)园细,如果緩存存在更新緩存惦积,如果緩存不存在加入到緩存中去。
這樣就體現(xiàn)出了猛频,責(zé)任鏈這樣實現(xiàn)的好處了狮崩,當(dāng)責(zé)任鏈執(zhí)行完畢蛛勉,如果攔截器想要拿到最終的數(shù)據(jù)做其他的邏輯處理等,這樣就不用在做其他的調(diào)用方法邏輯了睦柴,直接在當(dāng)前的攔截器就可以拿到最終的數(shù)據(jù)诽凌。
這也是okhttp設(shè)計的最優(yōu)雅最核心的功能。