OkHttp是一個高效的HTTP客戶端,它有以下默認特性:
- 支持HTTP/2,允許所有同一個主機地址的請求共享同一個socket連接
- 連接池減少請求延時
- 透明的GZIP壓縮減少響應(yīng)數(shù)據(jù)的大小
- 緩存響應(yīng)內(nèi)容,避免一些完全重復(fù)的請求
OKhttp的一般使用方法如下:
//異步執(zhí)行
private void asyncGetRequests() {
String url = "https://www.alibaba.com";
OkHttpClient okHttpClient = new OkHttpClient();
final Request request = new Request.Builder()
.url(url)
.get()//默認就是GET請求
.build();
Call call = okHttpClient.newCall(request);
call.enqueue(new Callback() { //執(zhí)行異步請求
@Override
public void onFailure(Call call, IOException e) {
do.....
}
@Override
public void onResponse(Call call, Response response) throws IOException {
do.....
}
});
}
//同步執(zhí)行
private void syncGetRequests() {
String url = "https://wwww.alibaba.com";
OkHttpClient okHttpClient = new OkHttpClient();
final Request request = new Request.Builder()
.url(url)
.get()//默認就是GET請求
.build();
final Call call = okHttpClient.newCall(request);
new Thread(() -> {
try {
Response response = call.execute(); //執(zhí)行同步請求
String result = response.body().string();
runOnUiThread(() -> tvContent.setText("synGetRequests run: " + result));
} catch (IOException e) {
e.printStackTrace();
}
}).start();
}
主要步驟
- 創(chuàng)建OkHttpClient和Request實例
- 通過OkHttpClient和Request的實例創(chuàng)建Call對象
- 通過調(diào)用call對象的enqueue或execute方法執(zhí)行異步或同步調(diào)用
整體流程
下圖展示了同步/異步請求的整體流程
類說明
- OkHttpClient:主要配置了網(wǎng)絡(luò)請求的很多參數(shù)催烘,最好是單例的形式存在,因為里面可以復(fù)用線程池和連接池。
- Call:一個接口類萧福,具體去執(zhí)行一個請求,只能被執(zhí)行一次辈赋,它由RealCall實現(xiàn)鲫忍。
- RealCall:定義了一個請求執(zhí)行的具體過程,包括同步/異步钥屈,并且定義了攔截器的排列順序悟民。
- AsyncCall:是RealCall的內(nèi)部類,它集成自NamedRunnable實現(xiàn)了Runnable篷就,所以它是在子線程運行射亏,里面具體處理了異步請求網(wǎng)絡(luò)執(zhí)行過程。
- Transmitter:意為發(fā)射器竭业,是應(yīng)用層和網(wǎng)絡(luò)層的橋梁智润,在進行連接、真正發(fā)出請求和讀取響應(yīng)中起到很重要的作用未辆,它里面保存了OkHttpClient窟绷、連接池、call咐柜、事件監(jiān)聽器兼蜈、call超時時間等信息。
- Dispatcher:調(diào)度器炕桨,主要是異步請求的并發(fā)控制饭尝、把異步請求放入線程池執(zhí)行,實現(xiàn)方法是promoteAndExecute()献宫。 promoteAndExecute()有兩處調(diào)用:添加異步請求時钥平、同步/異步請求 結(jié)束時,里面使用了幾個數(shù)組維護當前包含了那些Call,包括readyAsyncCalls涉瘾、runningAsyncCalls(最大值為64知态,這里限制了線程池產(chǎn)生線程的個數(shù))和runningSyncCalls。
- RealInterceptorChain:實現(xiàn)自Interceptor.Chain立叛,它是一個攔截器鏈负敏,它整合了所有攔截器,它具體實現(xiàn)了proceed方法秘蛇,在這個方法里面依次獲取了所有攔截器并執(zhí)行了攔截器的intercept方法其做,并傳入了下一個攔截器的索引。
流程中主要通過getResponseWithInterceptorChain方法獲取Response返回結(jié)果赁还,并且在這個方法里面整合了所有攔截器妖泄,我們具體看看整個過程。
攔截器組裝過程
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List<Interceptor> interceptors = new ArrayList<>();
interceptors.addAll(client.interceptors());//OkHttpClient.Build#addInterceptor添加自定義攔截器
interceptors.add(new RetryAndFollowUpInterceptor(client));//重試和重定向攔截器
interceptors.add(new BridgeInterceptor(client.cookieJar()));//應(yīng)用層和網(wǎng)絡(luò)層的橋接攔截器
interceptors.add(new CacheInterceptor(client.internalCache()));//緩存處理相關(guān)的攔截器
interceptors.add(new ConnectInterceptor(client));//連接服務(wù)的攔截器艘策,真正的網(wǎng)絡(luò)請求在這里實現(xiàn)
if (!forWebSocket) {
interceptors.addAll(client.networkInterceptors());//OkHttpClient.Build#addNetworkInterceptor添加自定義網(wǎng)絡(luò)網(wǎng)絡(luò)攔截器
}
interceptors.add(new CallServerInterceptor(forWebSocket));//負責寫請求和讀響應(yīng)的攔截器蹈胡,Okio主場
//攔截器鏈,這里index為0朋蔫,所以默認是先執(zhí)行interceptors中的第一個攔截器
Interceptor.Chain chain = new RealInterceptorChain(interceptors, transmitter, null, 0,
originalRequest, this, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());
boolean calledNoMoreExchanges = false;
try {
//攔截器鏈 開始執(zhí)行第一個index = 0 的攔截器
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);
}
}
}
通過上面的代碼我們可以知道罚渐,我們是通過一下順序加入的攔截器:
- 通過addInterceptor 方式加入的自定義攔截器
- RetryAndFollowUpInterceptor 重試和重定向攔截器
- BridgeInterceptor 橋攔截器
- CacheInterceptor 緩存攔截器
- ConnectInterceptor 連接攔截器
- 通過addNetworkInterceptor 方式加入的自定義攔截器
- CallServerInterceptor 請求服務(wù)攔截器
所有攔截器都實現(xiàn)Interceptor接口
Interceptor {
//把攔截器鏈傳遞下去,實現(xiàn)請求的處理過程驯妄,返回response
Response intercept(Chain chain) throws IOException;
//攔截器鏈荷并,負責把所有攔截器串聯(lián)起來
interface Chain {
Request request();
//Chain的核心方法,進行攔截器的層次調(diào)用
Response proceed(Request request) throws IOException;
//返回請求執(zhí)行的 連接. 僅網(wǎng)絡(luò)攔截器可用; 應(yīng)用攔截器就是null.
@Nullable Connection connection();
Call call();
int connectTimeoutMillis();
Chain withConnectTimeout(int timeout, TimeUnit unit);
int readTimeoutMillis();
Chain withReadTimeout(int timeout, TimeUnit unit);
int writeTimeoutMillis();
Chain withWriteTimeout(int timeout, TimeUnit unit);
}
}
Interceptor是個接口類富玷,只有一個intercept方法璧坟,參數(shù)是Chain對象。內(nèi)部接口類Chain攔截器鏈赎懦,有個proceed方法,參數(shù)是Request對象幻工,返回值是Response励两,這個方法的實現(xiàn)就是請求的處理過程了。
public Response proceed(Request request, Transmitter transmitter, @Nullable Exchange exchange)
throws IOException {
//省略部分代碼囊颅。当悔。。踢代。
// Call the next interceptor in the chain.
//獲取下一個攔截器的RealInterceptorChain對象盲憎,并執(zhí)行當前攔截器的intercept方法,并將chain作為參數(shù)傳入
RealInterceptorChain next = new RealInterceptorChain(interceptors, transmitter, exchange,
index + 1, request, call, connectTimeout, readTimeout, writeTimeout);
Interceptor interceptor = interceptors.get(index);
Response response = interceptor.intercept(next);
//省略部分代碼胳挎。饼疙。。慕爬。
return response;
}
綜上所述窑眯,攔截器通過chain的執(zhí)行過程類似于洋蔥屏积,從最外層,層層傳入磅甩,并重最內(nèi)層炊林,層層傳出。所有攔截器都可以處理當前獲取的request卷要,并將結(jié)果傳入內(nèi)層渣聚,也可以處理當前獲取的response,并將結(jié)果傳入最外層僧叉,除了最后一個攔截器饵逐,重這里我們也可以看出通過addInterceptor和addNetworkInterceptor的區(qū)別,前者更外層所以它只能處理最開始的request和最后的response彪标,整個請求過程調(diào)用一次倍权,而后者更內(nèi)層,所以它可以被調(diào)用多次捞烟,比如重試或者重定向后都會調(diào)用它薄声,并且它可以拿到很多connection和net相關(guān)的數(shù)據(jù)。
下面我們就依次分析每個攔截器题画。
重試/重定向攔截器
RetryAndFollowUpInterceptor默辨,意為“重試和重定向攔截器”,作用是連接失敗后進行重試苍息、對請求結(jié)果跟進后進行重定向缩幸,我們先看看它整個執(zhí)行過程,如下圖:
@Override public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
RealInterceptorChain realChain = (RealInterceptorChain) chain;
Transmitter transmitter = realChain.transmitter();
int followUpCount = 0;
Response priorResponse = null;
while (true) {
// 準備連接請求
// 主機竞思、端口表谊、協(xié)議都相同時,連接可復(fù)用
transmitter.prepareToConnect(request);
if (transmitter.isCanceled()) {
throw new IOException("Canceled");
}
Response response;
boolean success = false;
try {
// 執(zhí)行其他(下一個->下一個->...)攔截器的功能盖喷,獲取Response爆办;
response = realChain.proceed(request, transmitter, null);
success = true;
} catch (RouteException e) {
// 連接路由異常,此時請求還未發(fā)送课梳。嘗試恢復(fù)
// 返回true距辆,continue,繼續(xù)下一個while循環(huán)
// The attempt to connect via a route failed. The request will not have been sent.
if (!recover(e.getLastConnectException(), transmitter, false, request)) {
throw e.getFirstConnectException();
}
continue;
} catch (IOException e) {
// IO異常暮刃,請求可能已經(jīng)發(fā)出跨算。嘗試恢復(fù)
// An attempt to communicate with a server failed. The request may have been sent.
boolean requestSendStarted = !(e instanceof ConnectionShutdownException);
if (!recover(e, transmitter, requestSendStarted, request)) throw e;
continue;
} finally {
// 請求沒成功,釋放資源
// The network call threw an exception. Release any resources.
if (!success) {
transmitter.exchangeDoneDueToException();
}
}
// 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();
}
Exchange exchange = Internal.instance.exchange(response);
Route route = exchange != null ? exchange.connection().route() : null;
// 后面的攔截器執(zhí)行完了椭懊,拿到Response
// 解析看下是否需要重試或重定向诸蚕,需要則返回新的Request
Request followUp = followUpRequest(response, route);
if (followUp == null) {
if (exchange != null && exchange.isDuplex()) {
transmitter.timeoutEarlyExit();
}
// 如果followUpRequest返回的Request為空,那邊就表示不需要執(zhí)行重試或者重定向,直接返回數(shù)據(jù)
return response;
}
RequestBody followUpBody = followUp.body();
if (followUpBody != null && followUpBody.isOneShot()) {
// 如果followUp不為null挫望,請求體不為空立润,但只需要請求一次時,那么就返回response媳板;
return response;
}
closeQuietly(response.body());
if (transmitter.hasExchange()) {
exchange.detachWithViolence();
}
// 判斷重試或者重定向的次數(shù)是否超過最大的次數(shù)20桑腮,是的話則拋出異常
if (++followUpCount > MAX_FOLLOW_UPS) {
throw new ProtocolException("Too many follow-up requests: " + followUpCount);
}
// 將需要重試或者重定向的請求賦值給新的請求
// 繼續(xù)執(zhí)行新請求
request = followUp;
priorResponse = response;
}//...while (true)
}
總結(jié)
- 通過realChain.proceed發(fā)起請求獲取response
- 如果發(fā)生異常,則執(zhí)行recover方法判斷是否可以重試
- 如果請求成功蛉幸,則通過followUpRequest判斷response是否需要重定向
- 如果不需要重定向破讨,則請求成功返回
橋攔截器
BridgeInterceptor ,意為 橋攔截器奕纫,相當于 在 請求發(fā)起端 和 網(wǎng)絡(luò)執(zhí)行端 架起一座橋提陶,把應(yīng)用層發(fā)出的請求 變?yōu)?網(wǎng)絡(luò)層認識的請求,把網(wǎng)絡(luò)層執(zhí)行后的響應(yīng) 變?yōu)?應(yīng)用層便于應(yīng)用層使用的結(jié)果匹层。
public final class BridgeInterceptor implements Interceptor {
//Cookie管理器隙笆,初始化OkhttpClient時創(chuàng)建的
//默認是CookieJar.NO_COOKIES
private final CookieJar cookieJar;
public BridgeInterceptor(CookieJar cookieJar) {
this.cookieJar = cookieJar;
}
@Override public Response intercept(Chain chain) throws IOException {
Request userRequest = chain.request();
Request.Builder requestBuilder = userRequest.newBuilder();
RequestBody body = userRequest.body();
// 處理封裝"Content-Type", "Content-Length","Transfer-Encoding","Host","Connection",
// "Accept-Encoding","Cookie","User-Agent"等請求頭
if (body != null) {
MediaType contentType = body.contentType();
if (contentType != null) {
requestBuilder.header("Content-Type", contentType.toString());
}
long contentLength = body.contentLength();
if (contentLength != -1) {
requestBuilder.header("Content-Length", Long.toString(contentLength));
requestBuilder.removeHeader("Transfer-Encoding");
} else {
requestBuilder.header("Transfer-Encoding", "chunked");
requestBuilder.removeHeader("Content-Length");
}
}
if (userRequest.header("Host") == null) {
requestBuilder.header("Host", hostHeader(userRequest.url(), false));
}
if (userRequest.header("Connection") == null) {
requestBuilder.header("Connection", "Keep-Alive");
}
// 在服務(wù)器支持gzip壓縮的前提下,客戶端不設(shè)置Accept-Encoding=gzip的話升筏,
// okhttp會自動幫我們開啟gzip和解壓數(shù)據(jù)撑柔,如果客戶端自己開啟了gzip,就需要自己解壓服務(wù)器返回的數(shù)據(jù)了您访。
// If we add an "Accept-Encoding: gzip" header field we're responsible for also decompressing
// the transfer stream.
boolean transparentGzip = false;
if (userRequest.header("Accept-Encoding") == null && userRequest.header("Range") == null) {
transparentGzip = true;
requestBuilder.header("Accept-Encoding", "gzip");
}
//從cookieJar中獲取cookie铅忿,添加到header
List<Cookie> cookies = cookieJar.loadForRequest(userRequest.url());
if (!cookies.isEmpty()) {
requestBuilder.header("Cookie", cookieHeader(cookies));
}
if (userRequest.header("User-Agent") == null) {
requestBuilder.header("User-Agent", Version.userAgent());
}
// 把處理好的新請求往下傳遞,執(zhí)行后續(xù)的攔截器的邏輯
Response networkResponse = chain.proceed(requestBuilder.build());
//從networkResponse中獲取 header "Set-Cookie" 存入cookieJar
HttpHeaders.receiveHeaders(cookieJar, userRequest.url(), networkResponse.headers());
// 獲取返回體的Builder
Response.Builder responseBuilder = networkResponse.newBuilder()
.request(userRequest);
// 處理返回的Response的"Content-Encoding"灵汪、"Content-Length"檀训、"Content-Type"等返回頭
// 如果我們沒有手動添加"Accept-Encoding: gzip",這里會創(chuàng)建 能自動解壓的responseBody--GzipSource
if (transparentGzip
&& "gzip".equalsIgnoreCase(networkResponse.header("Content-Encoding"))
&& HttpHeaders.hasBody(networkResponse)) {
GzipSource responseBody = new GzipSource(networkResponse.body().source());
Headers strippedHeaders = networkResponse.headers().newBuilder()
.removeAll("Content-Encoding")
.removeAll("Content-Length")
.build();
responseBuilder.headers(strippedHeaders);
String contentType = networkResponse.header("Content-Type");
responseBuilder.body(new RealResponseBody(contentType, -1L, Okio.buffer(responseBody)));
}
//然后新構(gòu)建的response返回出去
return responseBuilder.build();
}
}
總結(jié)
主要就是添加了一些header享言、如果有cookie就添加峻凫、如果需要gzip就做壓縮和解壓縮。
緩存攔截器
CacheInterceptor担锤,緩存攔截器蔚晨,提供網(wǎng)絡(luò)請求緩存的存取。合理使用本地緩存肛循,有效地減少網(wǎng)絡(luò)開銷、減少響應(yīng)延遲银择。
public final class CacheInterceptor implements Interceptor {
final @Nullable InternalCache cache;
public CacheInterceptor(@Nullable InternalCache cache) {
this.cache = cache;
}
@Override public Response intercept(Chain chain) throws IOException {
// 先獲取候選緩存多糠,前提是有配置緩存,也就是cache不為空浩考;
Response cacheCandidate = cache != null
? cache.get(chain.request())
: null;
long now = System.currentTimeMillis();
// 執(zhí)行獲取緩存策略的邏輯
// 緩存策略決定是否使用緩存:
// strategy.networkRequest為null,不使用網(wǎng)絡(luò)
// strategy.cacheResponse為null,不使用緩存只怎。
CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();
// 網(wǎng)絡(luò)請求
Request networkRequest = strategy.networkRequest;
// 本地的緩存保存的請求
Response cacheResponse = strategy.cacheResponse;
//根據(jù)緩存策略更新統(tǒng)計指標:請求次數(shù)怜俐、網(wǎng)絡(luò)請求次數(shù)、使用緩存次數(shù)
if (cache != null) {
cache.trackResponse(strategy);
}
//有緩存 但不能用拍鲤,關(guān)掉
if (cacheCandidate != null && cacheResponse == null) {
closeQuietly(cacheCandidate.body()); // The cache candidate wasn't applicable. Close it.
}
// networkRequest == null 不能用網(wǎng)絡(luò)
// 如果不使用網(wǎng)絡(luò)數(shù)據(jù)且緩存數(shù)據(jù)為空,那么返回一個504的Response擅这,并且body為空
// 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();
}
// 如果不需要使用網(wǎng)絡(luò)數(shù)據(jù)景鼠,那么就直接返回緩存的數(shù)據(jù)
// If we don't need the network, we're done.
if (networkRequest == null) {
return cacheResponse.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.build();
}
/*
* 到這里仲翎,networkRequest != null (cacheResponse可能null,可能!null)
* 沒有命中強緩存的情況下铛漓,進行網(wǎng)絡(luò)請求溯香,獲取response
* 先判斷是否是協(xié)商緩存(304)命中,命中則更新緩存返回response
* 未命中使用網(wǎng)絡(luò)請求的response返回并添加緩存
*/
Response networkResponse = null;
try {
// 執(zhí)行后續(xù)的攔截器邏輯
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());
}
}
// If we have a cache response too, then we're doing a conditional get.
if (cacheResponse != null) {
// 如果緩存數(shù)據(jù)不為空并且code為304票渠,表示數(shù)據(jù)沒有變化逐哈,繼續(xù)使用緩存數(shù)據(jù);
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();
// 更新緩存數(shù)據(jù)
cache.update(cacheResponse, response);
return response;
} else {
//如果是非304问顷,說明服務(wù)端資源有更新昂秃,就關(guān)閉緩存body
closeQuietly(cacheResponse.body());
}
}
// 協(xié)商緩存也未命中,獲取網(wǎng)絡(luò)返回的response
Response response = networkResponse.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.networkResponse(stripBody(networkResponse))
.build();
if (cache != null) {
//網(wǎng)絡(luò)響應(yīng)可緩存(請求和響應(yīng)的 頭 Cache-Control都不是'no-store')
if (HttpHeaders.hasBody(response) && CacheStrategy.isCacheable(response, networkRequest)) {
// 將網(wǎng)絡(luò)數(shù)據(jù)保存到緩存中
// InternalCache接口杜窄,實現(xiàn)在Cache類中
// Offer this request to the cache.
CacheRequest cacheRequest = cache.put(response);
return cacheWritingResponse(cacheRequest, response);
}
//OkHttp默認只會對get請求進行緩存
//不是get請求就移除緩存
if (HttpMethod.invalidatesCache(networkRequest.method())) {
try {
// 緩存失效肠骆,那么就移除緩存
cache.remove(networkRequest);
} catch (IOException ignored) {
// The cache cannot be written.
}
}
}
return response;
}
...
}
總結(jié)
- 通過緩存策略決定是使用緩存數(shù)據(jù)還是網(wǎng)絡(luò)數(shù)據(jù)
- 默認只緩存GET請求
- 緩存保存在用戶指定的目錄,內(nèi)部通過DiskLruCache實現(xiàn)塞耕,使用URL的md5做為key
連接攔截器
ConnectInterceptor蚀腿,連接攔截器,主要負責連接的創(chuàng)建和復(fù)用莉钙。
@Override public Response intercept(Chain chain) throws IOException {
RealInterceptorChain realChain = (RealInterceptorChain) chain;
Request request = realChain.request();
Transmitter transmitter = realChain.transmitter();
// We need the network to satisfy this request. Possibly for validating a conditional GET.
boolean doExtensiveHealthChecks = !request.method().equals("GET");
// 實現(xiàn)了復(fù)雜的網(wǎng)絡(luò)請求,Exchange用于下一個攔截器CallServerInterceptor進行網(wǎng)絡(luò)請求使用筛谚;
/*
1:調(diào)用transmitter.newExchange方法蚊伞;
2:通過Transmitter的ExchangeFinder調(diào)用了find方法癞揉;
3:ExchangeFinder里調(diào)用了findHealthyConnection方法;
4:ExchangeFinder里findHealthyConnection調(diào)用了findConnection方法創(chuàng)建了RealConnection胳泉;
5:調(diào)用了RealConnection的connect方法控汉,實現(xiàn)了TCP + TLS 握手测僵,底層通過socket來實現(xiàn)的颜武;
6: 通過RealConnection的newCodec方法創(chuàng)建了兩個協(xié)議類鬼贱,一個是Http1ExchangeCodec蹋岩,對應(yīng)著HTTP1.1,
一個是Http2ExchangeCodec棋嘲,對應(yīng)著HTTP2.0;
*/
//創(chuàng)建一個交換器Exchange
Exchange exchange = transmitter.newExchange(chain, doExtensiveHealthChecks);
// 執(zhí)行后續(xù)的攔截器邏輯,注意是三個參數(shù)
return realChain.proceed(request, transmitter, exchange);
}
類說明
- Transmitter:意為發(fā)射器,是把請求從應(yīng)用端發(fā)射到網(wǎng)絡(luò)層,它持有請求的連接甩骏、請求论熙、響應(yīng)和流,一個請求對應(yīng)一個Transmitter實例交惯,一個數(shù)據(jù)流。
- Exchange:request、responseIO管理笤昨,對ExchangeCodec的包裝睡互,增加了事件回調(diào)妻怎,一個請求對應(yīng)一個Exchange實例。
- ExchangeCodec:接口類,負責真正的IO寫請求、讀響應(yīng)掖鱼,實現(xiàn)類有Http1ExchangeCodec褐墅、Http2ExchangeCodec码泛,對應(yīng)HTTP1.1齐莲、HTTP2.0岳枷。
- ExchangeFinder:從連接池中尋找可用TCP連接,然后通過連接得到ExchangeCodec。
- Route:一個具體連接服務(wù)器的路由拴竹,包括address(IP地址通過DNS可能獲取多個)和proxy(通過OKhttpclient配置沒有則使用默認的)
- RouteSelector:配置代理服務(wù)器以及通過DNS獲取IP地址resetNextInetSocketAddress。
- ConnectionPool:連接池,用于管理http1.1/http2.0連接重用赏陵,以減少網(wǎng)絡(luò)延遲。
相同Address的http請求可以共享一個連接,ConnectionPool就是實現(xiàn)了連接的復(fù)用。
1.默認最多保存5個空閑連接,并且空閑5分鐘后移除癌淮。
2.通過put方法給連接池添加連接虚倒,首先通過cleanupRunning線程移除空閑并過期的連
接易猫,這個線程在整個線程池里面只會存在一個准颓。
3.通過transmitterAcquirePooledConnection取一個可以復(fù)用的連接炮赦。 - RealConnection:一個具體的連接吠勘,通過connect闖將一個連接通道剧防,供后續(xù)IO操作使用。
總結(jié)
- 主要是獲取一個Exchange對象并通過realChain.proceed方法傳遞給后續(xù)攔截器做具體的IO操作使用棚唆。
- 通過ExchangeFinder去獲取一個ExchangeCodec鞋囊,即具體操作request和response的協(xié)議溜腐。
- 通過RealConnectionPool連接池去復(fù)用連接挺益,連接池復(fù)用邏輯如下圖所示:
請求服務(wù)連接器
CallServerInterceptor伞辛,請求服務(wù)攔截器蚤氏,也就是真正地去進行網(wǎng)絡(luò)IO讀寫了——寫入http請求的header和body數(shù)據(jù)竿滨、讀取響應(yīng)的header和body毁葱。
public final class CallServerInterceptor implements Interceptor {
private final boolean forWebSocket;
public CallServerInterceptor(boolean forWebSocket) {
this.forWebSocket = forWebSocket;
}
@Override public Response intercept(Chain chain) throws IOException {
RealInterceptorChain realChain = (RealInterceptorChain) chain;
//ConnectInterceptor攔截器傳入的exchange
Exchange exchange = realChain.exchange();
Request request = realChain.request();
long sentRequestMillis = System.currentTimeMillis();
// 將請求頭寫入到socket中,底層通過ExchangeCodec協(xié)議類
// (對應(yīng)Http1ExchangeCodec和Http2ExchangeCodec)
// 最終是通過Okio來實現(xiàn)的鸠澈,具體實現(xiàn)在RealBufferedSink這個類里面
exchange.writeRequestHeaders(request);
// 如果有body的話际度,通過Okio將body寫入到socket中,用于發(fā)送給服務(wù)器
boolean responseHeadersStarted = false;
Response.Builder responseBuilder = null;
//含body的請求
if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) {
// If there's a "Expect: 100-continue" header on the request, wait for a "HTTP/1.1 100
// Continue" response before transmitting the request body. If we don't get that, return
// what we did get (such as a 4xx response) without ever transmitting the request body.
// 若請求頭包含 "Expect: 100-continue" ,
// 就會等服務(wù)端返回含有 "HTTP/1.1 100 Continue"的響應(yīng)鹉勒,然后再發(fā)送請求body.
// 如果沒有收到這個響應(yīng)(例如收到的響應(yīng)是4xx),那就不發(fā)送body了脯倒。
if ("100-continue".equalsIgnoreCase(request.header("Expect"))) {
exchange.flushRequest();
responseHeadersStarted = true;
exchange.responseHeadersStart();
//讀取響應(yīng)頭
responseBuilder = exchange.readResponseHeaders(true);
}
// responseBuilder為null說明服務(wù)端返回了100藻丢,也就是可以繼續(xù)發(fā)送body了
// 底層通過ExchangeCodec協(xié)議類(對應(yīng)Http1ExchangeCodec和Http2ExchangeCodec)來讀取返回頭header的數(shù)據(jù)
if (responseBuilder == null) {
if (request.body().isDuplex()) {//默認是false不會進入
// Prepare a duplex body so that the application can send a request body later.
exchange.flushRequest();
BufferedSink bufferedRequestBody = Okio.buffer(
exchange.createRequestBody(request, true));
request.body().writeTo(bufferedRequestBody);
} else {
// Write the request body if the "Expect: 100-continue" expectation was met.
// 滿足了 "Expect: 100-continue" ,寫請求body
BufferedSink bufferedRequestBody = Okio.buffer(
exchange.createRequestBody(request, false));
request.body().writeTo(bufferedRequestBody);
bufferedRequestBody.close();
}
} else {
//沒有滿足 "Expect: 100-continue" ,請求發(fā)送結(jié)束
exchange.noRequestBody();
if (!exchange.connection().isMultiplexed()) {
// If the "Expect: 100-continue" expectation wasn't met, prevent the HTTP/1 connection
// from being reused. Otherwise we're still obligated to transmit the request body to
// leave the connection in a consistent state.
exchange.noNewExchangesOnConnection();
}
}
} else {
//沒有body,請求發(fā)送結(jié)束
exchange.noRequestBody();
}
//請求發(fā)送結(jié)束
if (request.body() == null || !request.body().isDuplex()) {
//真正將寫到socket輸出流的http請求數(shù)據(jù)發(fā)送冰木。
exchange.finishRequest();
}
//回調(diào) 讀響應(yīng)頭開始事件(如果上面沒有)
if (!responseHeadersStarted) {
exchange.responseHeadersStart();
}
//讀響應(yīng)頭(如果上面沒有)
if (responseBuilder == null) {
responseBuilder = exchange.readResponseHeaders(false);
}
// 創(chuàng)建返回體Response
Response response = responseBuilder
.request(request)
.handshake(exchange.connection().handshake())
.sentRequestAtMillis(sentRequestMillis)
.receivedResponseAtMillis(System.currentTimeMillis())
.build();
int code = response.code();
if (code == 100) {
// server sent a 100-continue even though we did not request one.
// try again to read the actual response
// 服務(wù)端又返回了個100穷劈,就再嘗試獲取真正的響應(yīng)
response = exchange.readResponseHeaders(false)
.request(request)
.handshake(exchange.connection().handshake())
.sentRequestAtMillis(sentRequestMillis)
.receivedResponseAtMillis(System.currentTimeMillis())
.build();
code = response.code();
}
//回調(diào)讀響應(yīng)頭結(jié)束
exchange.responseHeadersEnd(response);
//這里就是獲取響應(yīng)body了
if (forWebSocket && code == 101) {
// Connection is upgrading, but we need to ensure interceptors see a non-null response body.
response = response.newBuilder()
.body(Util.EMPTY_RESPONSE)
.build();
} else {
// 讀取返回體body的數(shù)據(jù)
// 底層通過ExchangeCodec協(xié)議類(對應(yīng)Http1ExchangeCodec和Http2ExchangeCodec)
response = response.newBuilder()
.body(exchange.openResponseBody(response))
.build();
}
//請求頭中Connection是close,表示請求完成后要關(guān)閉連接
if ("close".equalsIgnoreCase(response.request().header("Connection"))
|| "close".equalsIgnoreCase(response.header("Connection"))) {
exchange.noNewExchangesOnConnection();
}
//204(無內(nèi)容)踊沸、205(重置內(nèi)容)歇终,body應(yīng)該是空
if ((code == 204 || code == 205) && response.body().contentLength() > 0) {
throw new ProtocolException(
"HTTP " + code + " had non-zero Content-Length: " + response.body().contentLength());
}
return response;
}
}
類說明
- Exchange:對外提供了writeXX、openXX逼龟、flushXX等方法通過ExchangeCodec操作request和response的傳遞评凝。
- ExchangeCodec:它有兩個實現(xiàn)類Http1ExchangeCodec(HTTP/1.1)和Http2ExchangeCodec(HTTP/2),里面根據(jù)不同的協(xié)議規(guī)范制定了具體的格式和IO過程腺律。
- Okio:具體執(zhí)行IO操作的框架
總結(jié)
- 通過上面的connection通道日杈,具體執(zhí)行發(fā)送request和接受response過程涨冀。
- 這個攔截器最終返回了response恋拍,并且沒有執(zhí)行chain的proceed方法僵娃,所以它是最里層節(jié)點,然后一層層把response返回出去。