1.okhttp源碼分析(一)——基本流程(超詳細(xì))
2.okhttp源碼分析(二)——RetryAndFollowUpInterceptor過(guò)濾器
3.okhttp源碼分析(三)——CacheInterceptor過(guò)濾器
4.okhttp源碼分析(四)——ConnectInterceptor過(guò)濾器
5.okhttp源碼分析(五)——CallServerInterceptor過(guò)濾器
前言
終于到最后一個(gè)過(guò)濾器了,其實(shí)閱讀源碼的過(guò)程中有過(guò)想要放棄的想法,因?yàn)橥ㄟ^(guò)查看OkHttp的源碼隨著逐步深入會(huì)逐漸發(fā)現(xiàn)OkHttp其實(shí)相較于Volley來(lái)說(shuō)更像一個(gè)對(duì)于Http協(xié)議封裝優(yōu)化的一個(gè)網(wǎng)絡(luò)框架,從請(qǐng)求的開(kāi)始一直延伸到到Socket的建立和通信,閱讀起來(lái)給人感覺(jué)更枯燥。
從過(guò)濾器的責(zé)任鏈一直到這個(gè)過(guò)濾器,這個(gè)過(guò)濾器的功能其實(shí)就是負(fù)責(zé)網(wǎng)絡(luò)通信最后一個(gè)步驟:數(shù)據(jù)交換,也就是負(fù)責(zé)向服務(wù)器發(fā)送請(qǐng)求數(shù)據(jù)、從服務(wù)器讀取響應(yīng)數(shù)據(jù)(實(shí)際網(wǎng)絡(luò)請(qǐng)求)。
分析
1.宏觀流程
直接上簡(jiǎn)化過(guò)的代碼吧
@Override public Response intercept(Chain chain) throws IOException {
//開(kāi)始寫(xiě)入header
httpCodec.writeRequestHeaders(request);
//當(dāng)Header為Expect: 100-continue時(shí),只發(fā)送請(qǐng)求頭
if ("100-continue".equalsIgnoreCase(request.header("Expect"))) {
httpCodec.flushRequest();
responseBuilder = httpCodec.readResponseHeaders(true);
}
//寫(xiě)入請(qǐng)求體
request.body().writeTo(bufferedRequestBody);
//寫(xiě)入完成
...
//結(jié)束請(qǐng)求
httpCodec.finishRequest();
//得到響應(yīng)頭
responseBuilder = httpCodec.readResponseHeaders(false);
//構(gòu)建初步響應(yīng)
Response response = responseBuilder
.request(request)
.handshake(streamAllocation.connection().handshake())
.sentRequestAtMillis(sentRequestMillis)
.receivedResponseAtMillis(System.currentTimeMillis())
.build();
...
//構(gòu)建響應(yīng)體
response = response.newBuilder()
.body(httpCodec.openResponseBody(response))
.build();
}
。。。
//返回響應(yīng)
return response;
}
這里流程上其實(shí)就比較清楚了,這個(gè)過(guò)濾器的作用是數(shù)據(jù)交換,也就會(huì)說(shuō)發(fā)送請(qǐng)求和得到響應(yīng)。
這里大致流程分為:
1.先寫(xiě)入請(qǐng)求Header
2.如果請(qǐng)求頭的Expect: 100-continue時(shí),只發(fā)送請(qǐng)求頭,執(zhí)行3,不然執(zhí)行4
3.根據(jù)后臺(tái)返回的結(jié)果判斷是否繼續(xù)請(qǐng)求流程
4.寫(xiě)入請(qǐng)求體,完成請(qǐng)求
5.得到響應(yīng)頭,構(gòu)建初步響應(yīng)
6.構(gòu)建響應(yīng)體,完成最終響應(yīng)
7.返回響應(yīng)
2.過(guò)程細(xì)節(jié)
1.寫(xiě)入Header
//開(kāi)始寫(xiě)入header
realChain.eventListener().requestHeadersStart(realChain.call());
httpCodec.writeRequestHeaders(request);
//寫(xiě)入結(jié)束
realChain.eventListener().requestHeadersEnd(realChain.call(), request);
其實(shí)通過(guò)事件回調(diào)就可以看出來(lái)這個(gè)步驟的作用是寫(xiě)入Header,這里調(diào)用了HttpCodec的writeRequestHeaders的方法,看過(guò)前面分析的應(yīng)該知道HttpCodec其實(shí)是一個(gè)接口,對(duì)應(yīng)的使用策略模式分別根據(jù)是Http還是Http/2請(qǐng)求,這里就看一下Http1Codec的實(shí)現(xiàn)吧。
@Override public void writeRequestHeaders(Request request) throws IOException {
String requestLine = RequestLine.get(
request, streamAllocation.connection().route().proxy().type());
//寫(xiě)入header
writeRequest(request.headers(), requestLine);
}
//RequestLine.java
/**
* Returns the request status line, like "GET / HTTP/1.1". This is exposed to the application by
* {@link HttpURLConnection#getHeaderFields}, so it needs to be set even if the transport is
* HTTP/2.
*/
//構(gòu)建"GET / HTTP/1.1"形式的請(qǐng)求狀態(tài)行
public static String get(Request request, Proxy.Type proxyType) {
StringBuilder result = new StringBuilder();
result.append(request.method());
result.append(' ');
if (includeAuthorityInRequestLine(request, proxyType)) {
result.append(request.url());
} else {
result.append(requestPath(request.url()));
}
result.append(" HTTP/1.1");
return result.toString();
}
其實(shí)代碼還是比較好理解的,這里可以看到首先調(diào)用RequestLine的get方法獲得一個(gè)請(qǐng)求行,這里方法源碼也放上來(lái)了,其實(shí)代碼很好理解,可以根據(jù)注釋或者自己看代碼,都會(huì)發(fā)現(xiàn)最后返回的是類(lèi)似于"GET / HTTP/1.1"
的字符串。最后調(diào)用writeRequest方法寫(xiě)入Header信息。
/** Returns bytes of a request header for sending on an HTTP transport. */
public void writeRequest(Headers headers, String requestLine) throws IOException {
//寫(xiě)入header
if (state != STATE_IDLE) throw new IllegalStateException("state: " + state);
//Okio中的sink,底層是socket使用的是okio優(yōu)化
sink.writeUtf8(requestLine).writeUtf8("\r\n");
for (int i = 0, size = headers.size(); i < size; i++) {
sink.writeUtf8(headers.name(i))
.writeUtf8(": ")
.writeUtf8(headers.value(i))
.writeUtf8("\r\n");
}
sink.writeUtf8("\r\n");
state = STATE_OPEN_REQUEST_BODY;
}
可以看到這里其實(shí)沒(méi)有什么復(fù)雜的邏輯,就是遍歷Header,然后通過(guò)前一個(gè)過(guò)濾器建立的連接得到的Sink,來(lái)進(jìn)行寫(xiě)操作,這也是為什么OkHttp依賴于Okio。這里還有一個(gè)可以注意的地方,這里最后將state變量賦值為了STATE_OPEN_REQUEST_BODY,其實(shí)會(huì)發(fā)現(xiàn),隨著方法的 進(jìn)行,這個(gè)state變量會(huì)從上一個(gè)狀態(tài)變?yōu)橄乱粋€(gè)狀態(tài),這里其實(shí)用到了狀態(tài)模式的思想,雖然沒(méi)有那么細(xì)致將每個(gè)狀態(tài)分出來(lái),但是狀態(tài)模式的思想,狀態(tài)的改變還是用到的。
2.對(duì)于“Expect:100-continue”情況的處理
Response.Builder responseBuilder = null;
if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) {
if ("100-continue".equalsIgnoreCase(request.header("Expect"))) {
httpCodec.flushRequest();
realChain.eventListener().responseHeadersStart(realChain.call());
responseBuilder = httpCodec.readResponseHeaders(true);
}
這里先說(shuō)明一下是什么情況:
當(dāng)Header為Expect: 100-continue時(shí),只發(fā)送請(qǐng)求頭
- 發(fā)送一個(gè)請(qǐng)求, 包含一個(gè)Expect:100-continue, 詢問(wèn)Server使用愿意接受數(shù)據(jù)
- 接收到Server返回的100-continue應(yīng)答以后, 才把數(shù)據(jù)POST給Server
所以對(duì)應(yīng)注意幾個(gè)點(diǎn):
1.Response.Builder responseBuilder = null;
首先構(gòu)建了一個(gè)null的responseBuilder。
2.執(zhí)行httpCodec.flushRequest()
刷新請(qǐng)求。
3.執(zhí)行httpCodec.readResponseHeaders(true);
讀取response的header信息,并返回一個(gè)responseBuilder賦值給responseBuilder。
這里看一下readResponseHeaders方法。
@Override public Response.Builder readResponseHeaders(boolean expectContinue) throws IOException {
if (state != STATE_OPEN_REQUEST_BODY && state != STATE_READ_RESPONSE_HEADERS) {
throw new IllegalStateException("state: " + state);
}
try {
StatusLine statusLine = StatusLine.parse(readHeaderLine());
//構(gòu)建一個(gè)reponseBuilder
Response.Builder responseBuilder = new Response.Builder()
.protocol(statusLine.protocol)
.code(statusLine.code)
.message(statusLine.message)
.headers(readHeaders());
//如果得到的返回碼是可以繼續(xù)訪問(wèn),返回null
if (expectContinue && statusLine.code == HTTP_CONTINUE) {
return null;
}
state = STATE_OPEN_RESPONSE_BODY;
//不然返回構(gòu)建出來(lái)的reponseBuilder
return responseBuilder;
} catch (EOFException e) {
// Provide more context if the server ends the stream before sending a response.
IOException exception = new IOException("unexpected end of stream on " + streamAllocation);
exception.initCause(e);
throw exception;
}
}
這里其實(shí)就看我標(biāo)注的三處注釋,可以看到得到Response的Header后,第一步:構(gòu)建了一個(gè)ResponseBulder。第二步:通過(guò)返回的狀態(tài)碼判斷是否請(qǐng)求可以繼續(xù)進(jìn)行(“Expect:100-continue”情況的處理),如果可以返回null.第三步:否則,也就是不可以,返回構(gòu)建的ResponseBuilder。
所以綜上就可以看出來(lái),對(duì)于“Expect:100-continue”情況的處理總體為:
1.如果可以繼續(xù)請(qǐng)求,則responseBuilder=null
2.如果不行,則responseBuilder不為空,并且為返回的Header
3.寫(xiě)入請(qǐng)求體
if (responseBuilder == null) {
//得到響應(yīng)后,根據(jù)Resposne判斷是否寫(xiě)入請(qǐng)求體
// Write the request body if the "Expect: 100-continue" expectation was met.
//寫(xiě)入請(qǐng)求體
realChain.eventListener().requestBodyStart(realChain.call());
long contentLength = request.body().contentLength();
CountingSink requestBodyOut =
new CountingSink(httpCodec.createRequestBody(request, contentLength));
BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);
request.body().writeTo(bufferedRequestBody);
bufferedRequestBody.close();
//寫(xiě)入完成
realChain.eventListener()
.requestBodyEnd(realChain.call(), requestBodyOut.successfulCount);
} else if (!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.
streamAllocation.noNewStreams();
}
上面的步驟2看完后,這一步就很好理解了,如果可以繼續(xù)請(qǐng)求,則Responsebuilder=null,執(zhí)行if判斷里的內(nèi)容,可以看到就是對(duì)于請(qǐng)求體的寫(xiě)入操作,當(dāng)然任然是使用Okio進(jìn)行寫(xiě)入操作。
4.構(gòu)建響應(yīng)頭
//結(jié)束請(qǐng)求
httpCodec.finishRequest();
if (responseBuilder == null) {
//得到響應(yīng)頭
realChain.eventListener().responseHeadersStart(realChain.call());
responseBuilder = httpCodec.readResponseHeaders(false);
}
Response response = responseBuilder
.request(request)
.handshake(streamAllocation.connection().handshake())
.sentRequestAtMillis(sentRequestMillis)
.receivedResponseAtMillis(System.currentTimeMillis())
.build();
realChain.eventListener()
.responseHeadersEnd(realChain.call(), response);
延續(xù)上面的,當(dāng)寫(xiě)入請(qǐng)求體后,當(dāng)然就是得到響應(yīng)了,由于這里的responseBuilder仍然為null,所以執(zhí)行的還是我們上面步驟2分析過(guò)的方法httpCodec.readResponseHeaders(false);
,這時(shí)再通過(guò)返回得到的responseBuilder構(gòu)建攜帶有響應(yīng)頭的Reponse。
5.構(gòu)建響應(yīng)體,返回響應(yīng)
int code = response.code();
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 {
//構(gòu)建響應(yīng)體
response = response.newBuilder()
.body(httpCodec.openResponseBody(response))
.build();
}
if ("close".equalsIgnoreCase(response.request().header("Connection"))
|| "close".equalsIgnoreCase(response.header("Connection"))) {
streamAllocation.noNewStreams();
}
if ((code == 204 || code == 205) && response.body().contentLength() > 0) {
throw new ProtocolException(
"HTTP " + code + " had non-zero Content-Length: " + response.body().contentLength());
}
//返回響應(yīng)
return response;
剩下的其實(shí)就是對(duì)弈返回碼的判斷,對(duì)應(yīng)正常的返回碼的話,構(gòu)建響應(yīng)體到Response中,最后將Response返回。
終于---------------------------------------------------------------------------------->結(jié)束
總結(jié):
歷經(jīng)5篇博客,初步把okHttp的過(guò)濾器全部發(fā)全部分析完了,期間有過(guò)想要放棄,因?yàn)檎嬲催^(guò)源碼的應(yīng)該會(huì)明白我的感受吧。OkHttp其實(shí)從層面上來(lái)說(shuō)給我的感覺(jué)相較于Volley更底層一些,級(jí)別類(lèi)同于HttpURLConnection,將網(wǎng)絡(luò)協(xié)議用代碼進(jìn)行實(shí)現(xiàn),并層層優(yōu)化,直到最后的Socket用了Okio進(jìn)行替換。
有時(shí)會(huì)有將Volley,retrofit,Okhttp進(jìn)行比較優(yōu)劣的,其實(shí)給我的感覺(jué),Volley和Retrofit屬于一個(gè)層面,OkHttp相較于前兩個(gè)屬于更深的層面,完全可以替換一下Volley底層執(zhí)行網(wǎng)絡(luò)協(xié)議的內(nèi)核,替換為OkHttp,retrofit更不用說(shuō),底層就是okHttp,但是retrofit和volley相比哪,拋開(kāi)okHttp這個(gè)因素,其實(shí)各有優(yōu)劣,根據(jù)自己的需要吧,如果想要rxjava,耦合度極低,適合拓展,接口RESTful,當(dāng)然retrofit歡迎你。
通過(guò)看OkHttp的源碼除了原理外,其實(shí)可以發(fā)現(xiàn)許多其他的東西:
1.首先發(fā)現(xiàn)了計(jì)算機(jī)網(wǎng)絡(luò)的重要性,Http協(xié)議,準(zhǔn)備后面入一本書(shū),再回顧學(xué)習(xí)一下。
2.線程安全各種方式,各種數(shù)據(jù)結(jié)構(gòu)。
3.設(shè)計(jì)模式,粗略回顧一下:建造者模式,責(zé)任鏈模式,策略模式...其實(shí)不用強(qiáng)行使用設(shè)計(jì)模式,其實(shí)主要是設(shè)計(jì)思想
4.面向接口編程,這個(gè)不用說(shuō),越看越重要。
5.方法的命名,其實(shí)我感覺(jué)挺有趣,也挺講究的。
6.注釋,其實(shí)和5差不多,基本上就是編程風(fēng)格了。