okhttp原始碼解析
前言
http請求的功能應該是很簡單,只是為了魯棒性和效能需要寫很多的程式碼,發現okhttp還是挺複雜的,但是我們這裡還是要好好的搞定他。
正文
我們從最簡單的使用開始
OkHttpClient client = new OkHttpClient(); Request request = new Request.Builder() .url(url) .build(); Response response = client.newCall(request).execute()) { return response.body().string();
OkHttpClient,就是一個客戶操作的控制代碼,肯定是初始化了一大堆的預設引數,看先原始碼
public OkHttpClient() {
this(new Builder());
}
OkHttpClient(Builder builder) {
}
看到builder,我們就放心了(而不是一個庫會有兩種風格)。直接new出來,大概就是全部用預設引數,request大概也是那樣,我們還是直接看client的操作。 下面我們看真的請求過程。
client.newCall(request).execute()
我們看下 到底是如何創造出一個call
的。主要程式碼如下:
RealCall call = new RealCall(client, originalRequest, forWebSocket);
//這個暫時沒啥用,是個空的類
call.eventListener = client.eventListenerFactory().create(call);
其實就是一個client有一個RealCall
的類似建構函式的東西,.eventListener
是現在是個空,啥也不幹。現在回到一個最終極的複雜的問題,RealCall
的execute()
。
@Override
public Response execute() throws IOException {
......
//這段diamante比較奇怪,暫時沒搞懂到底要幹嘛,貌似是是為了呼叫週期。
client.dispatcher().executed(this);
Response result = getResponseWithInterceptorChain();
return result;
......
}
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));
RealInterceptorChain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
originalRequest, this, eventListener, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());
return chain.proceed(originalRequest);
}
看到後半部分才是核心啊,這裡搞了一個職責鏈模式,最後通過這個東東來完成所有請求的內容,這裡我們還是要好好的研讀一番。RealInterceptorChain
這個東西其實是一個用來遞迴呼叫的類,我們具體來看他的proceed
函式.
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
RealConnection connection) throws IOException {
......
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);
//這裡類似一個遞迴呼叫,第一個interceptor種有個迴圈,用來遞迴呼叫此函式,返回的時候,是真的response部為空的時候,
Response response = interceptor.intercept(next);
return response;
}
其實我們很容易發現這個東西,就是在new一個自己(當然因為職責鏈已經開始工作,所以只用後一個index即可),然後傳入給第一個職責者即可,我們看下第一個職責者做了什麼,是不是又回到這個函式,驗證我們這是一個迴圈的猜想,第一個是RetryAndFollowUpInterceptor
@Override
public Response intercept(Chain chain) throws IOException {
......
//這是職責鏈模式,第一個只是為了處理錯誤等,所以出現一個迴圈,其他的任何一個攔截器,出現問題,都應該在這裡重新處理。
while (true) {
......
response = realChain.proceed(request, streamAllocation, null, null);
}
......
}
這個函式灰常複雜,這裡暫時不在詳細研究,總之就是又一次遞迴呼叫我我們剛才RealInterceptorChain的process方法。那麼根據之前,他會呼叫了第二個職責者。因為這個主要是用來處理異常情況,我們不在詳細分析程式碼。我們直接分析後面真正的網路強求部分的職責。第二個職責者是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.
*/
@Override
public Response intercept(Chain chain) throws IOException {
Request userRequest = chain.request();
Request.Builder requestBuilder = userRequest.newBuilder();
RequestBody body = userRequest.body();
......
初始化請求的host
if (userRequest.header("Host") == null) {
requestBuilder.header("Host", hostHeader(userRequest.url(), false));
}
//這些都是請求的一些引數,可以暫時無視。
if (userRequest.header("Connection") == null) {
requestBuilder.header("Connection", "Keep-Alive");
}
// 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");
}
//Cookie這裡已經load,有些內容可以不用請求
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());
}
//chain 已經改變了,這裡request 也已經改變了。這裡又到了職責鏈的下一步,用來請求後來者來處理,
Response networkResponse = chain.proceed(requestBuilder.build());
}
註釋比較明確,者是一個橋接使用者和網路請求的中間者,主要是生成一些網路請求的內容,我們可以看程式碼,他確實是初始化一些請求的資訊,比如host,Accept-Encoding等變數。這裡我們不在詳細介紹。我們直接看下一個職責者。這是CacheInterceptor
/** Serves requests from the cache and writes responses to the cache. */
@Override public Response intercept(Chain chain) throws IOException {
......
//這裡其實就是剛才初始化完成的東東。不需要介意。
Request networkRequest = strategy.networkRequest;
......
networkResponse = chain.proceed(networkRequest);
......
}
註釋那麼明確,從註釋中看到這就是從cache中載入請求內容,和吧載入的內容寫入cache.cache這些東東不太是我們關注的焦點,我們暫時先放下吧,直接進入下一個請求部分。
/** Opens a connection to the target server and proceeds to the next interceptor. */
public Response intercept(Chain chain) throws IOException {
RealInterceptorChain realChain = (RealInterceptorChain) chain;
Request request = realChain.request();
StreamAllocation streamAllocation = realChain.streamAllocation();
// We need the network to satisfy this request. Possibly for validating a conditional GET.
boolean doExtensiveHealthChecks = !request.method().equals("GET");
HttpCodec httpCodec = streamAllocation.newStream(client, chain, doExtensiveHealthChecks);
RealConnection connection = streamAllocation.connection();
return realChain.proceed(request, streamAllocation, httpCodec, connection);
}
這個類的註釋也比較明確。開啟一個連線,然後交給後面的職責這幹活。emm這程式碼很少,確實非常複雜複雜,不想讀,不過大概就是建立一個連線,我們可以看到在StreamLoaction中可以看到
result = new RealConnection(connectionPool, selectedRoute);
......
result.connect(connectTimeout, readTimeout, writeTimeout, pingIntervalMillis,
connectionRetryEnabled, call, eventListener);
這是一個建立連線的部分。這裡我只是寫了部分程式碼,有些不想過分追究,有興趣的童鞋的可以斷點一下,這主要是為了建立一個ssl安全驗證的socket,這裡實在懶得過分追問,反正就是獲取一個socket的stream我們繼續看我們的真的請求的部分。
最後是一個CallServerInterceptor
/** This is the last interceptor in the chain. It makes a network call to the server. */
public Response intercept(Chain chain) throws IOException {
RealInterceptorChain realChain = (RealInterceptorChain) chain;
HttpCodec httpCodec = realChain.httpStream();
StreamAllocation streamAllocation = realChain.streamAllocation();
RealConnection connection = (RealConnection) realChain.connection();
Request request = realChain.request();
long sentRequestMillis = System.currentTimeMillis();
realChain.eventListener().requestHeadersStart(realChain.call());
httpCodec.writeRequestHeaders(request);
realChain.eventListener().requestHeadersEnd(realChain.call(), request);
Response.Builder responseBuilder = null;
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.
if ("100-continue".equalsIgnoreCase(request.header("Expect"))) {
httpCodec.flushRequest();
realChain.eventListener().responseHeadersStart(realChain.call());
responseBuilder = httpCodec.readResponseHeaders(true);
}
if (responseBuilder == null) {
// Write the request body if the "Expect: 100-continue" expectation was met.
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();
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();
}
}
httpCodec.finishRequest();
if (responseBuilder == null) {
realChain.eventListener().responseHeadersStart(realChain.call());
responseBuilder = httpCodec.readResponseHeaders(false);
}
Response response = responseBuilder
.request(request)
.handshake(streamAllocation.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
responseBuilder = httpCodec.readResponseHeaders(false);
response = responseBuilder
.request(request)
.handshake(streamAllocation.connection().handshake())
.sentRequestAtMillis(sentRequestMillis)
.receivedResponseAtMillis(System.currentTimeMillis())
.build();
code = response.code();
}
realChain.eventListener()
.responseHeadersEnd(realChain.call(), response);
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 {
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());
}
return response;
}
註釋告訴我們這就是真的網路請求了,媽的,到底如何請求的我就看不懂了,如果想完全搞懂,還是需要從倒數第二部,一點一點的斷點嘍。具體的http的請求過程。有機會再慢慢的驗證,這裡我就不管嘍。以後慢慢分析到底如何建立socket的。
後記
這個東西寫了挺久的,中間耽誤了一週,這次湊數把它完成,希望以後可以補充完整。