OkHttp面試之--HttpEngine中的readResponse流程簡介
阿新 • • 發佈:2018-12-30
上一節主要大體看了一下sendRequest的流程,本節來看一下當請求傳送出去之後,是如果讀取請求體中的資料的,具體的程式碼都在HttpEngine.readResponse方法中,程式碼如下:
public void readResponse() throws IOException {
if (userResponse != null) {
return; // Already ready.
}
if (networkRequest == null && cacheResponse == null) {
throw new IllegalStateException("call sendRequest() first!" );
}
if (networkRequest == null) {
return; // No network response to read.
}
Response networkResponse;
if (forWebSocket) {
httpStream.writeRequestHeaders(networkRequest);
networkResponse = readNetworkResponse();
} else if (!callerWritesRequestBody) {
networkResponse = new NetworkInterceptorChain(0, networkRequest).proceed(networkRequest);
} else {
// Emit the request body's buffer so that everything is in requestBodyOut.
if (bufferedRequestBody != null && bufferedRequestBody.buffer().size() > 0) {
bufferedRequestBody.emit();
}
// Emit the request headers if we haven't yet. We might have just learned the Content-Length.
if (sentRequestMillis == -1) {
if (OkHeaders.contentLength(networkRequest) == -1
&& requestBodyOut instanceof RetryableSink) {
long contentLength = ((RetryableSink) requestBodyOut).contentLength();
networkRequest = networkRequest.newBuilder()
.header("Content-Length", Long.toString(contentLength))
.build();
}
httpStream.writeRequestHeaders(networkRequest);
}
// Write the request body to the socket.
if (requestBodyOut != null) {
if (bufferedRequestBody != null) {
// This also closes the wrapped requestBodyOut.
bufferedRequestBody.close();
} else {
requestBodyOut.close();
}
if (requestBodyOut instanceof RetryableSink) {
httpStream.writeRequestBody((RetryableSink) requestBodyOut);
}
}
networkResponse = readNetworkResponse();
}
receiveHeaders(networkResponse.headers());
// If we have a cache response too, then we're doing a conditional get.
if (cacheResponse != null) {
if (validate(cacheResponse, networkResponse)) {
userResponse = cacheResponse.newBuilder()
.request(userRequest)
.priorResponse(stripBody(priorResponse))
.headers(combine(cacheResponse.headers(), networkResponse.headers()))
.cacheResponse(stripBody(cacheResponse))
.networkResponse(stripBody(networkResponse))
.build();
networkResponse.body().close();
releaseStreamAllocation();
// Update the cache after combining headers but before stripping the
// Content-Encoding header (as performed by initContentStream()).
InternalCache responseCache = Internal.instance.internalCache(client);
responseCache.trackConditionalCacheHit();
responseCache.update(cacheResponse, stripBody(userResponse));
userResponse = unzip(userResponse);
return;
} else {
closeQuietly(cacheResponse.body());
}
}
userResponse = networkResponse.newBuilder()
.request(userRequest)
.priorResponse(stripBody(priorResponse))
.cacheResponse(stripBody(cacheResponse))
.networkResponse(stripBody(networkResponse))
.build();
if (hasBody(userResponse)) {
maybeCache();
userResponse = unzip(cacheWritingResponse(storeRequest, userResponse));
}
}
從開始進行分析
此處的userResponse在第一次訪問時是null的,因此不會進入此程式碼塊,實際會進入如下判斷語句塊中
可以看到此處又呼叫了一個連線攔截器,對請求結果進行攔截,NetworkInterceptorChain也是實現了Intercept.Chain, 具體程式碼
class NetworkInterceptorChain implements Interceptor.Chain {
private final int index;
private final Request request;
private int calls;
NetworkInterceptorChain(int index, Request request) {
this.index = index;
this.request = request;
}
@Override public Connection connection() {
return streamAllocation.connection();
}
@Override public Request request() {
return request;
}
@Override public Response proceed(Request request) throws IOException {
calls++;
if (index > 0) {
Interceptor caller = client.networkInterceptors().get(index - 1);
Address address = connection().route().address();
// Confirm that the interceptor uses the connection we've already prepared.
if (!request.url().host().equals(address.url().host())
|| request.url().port() != address.url().port()) {
throw new IllegalStateException("network interceptor " + caller
+ " must retain the same host and port");
}
// Confirm that this is the interceptor's first call to chain.proceed().
if (calls > 1) {
throw new IllegalStateException("network interceptor " + caller
+ " must call proceed() exactly once");
}
}
if (index < client.networkInterceptors().size()) {
// There's another interceptor in the chain. Call that.
NetworkInterceptorChain chain = new NetworkInterceptorChain(index + 1, request);
Interceptor interceptor = client.networkInterceptors().get(index);
Response interceptedResponse = interceptor.intercept(chain);
// Confirm that the interceptor made the required call to chain.proceed().
if (chain.calls != 1) {
throw new IllegalStateException("network interceptor " + interceptor
+ " must call proceed() exactly once");
}
if (interceptedResponse == null) {
throw new NullPointerException("network interceptor " + interceptor
+ " returned null");
}
return interceptedResponse;
}
httpStream.writeRequestHeaders(request);
//Update the networkRequest with the possibly updated interceptor request.
networkRequest = request;
if (permitsRequestBody(request) && request.body() != null) {
Sink requestBodyOut = httpStream.createRequestBody(request, request.body().contentLength());
BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);
request.body().writeTo(bufferedRequestBody);
bufferedRequestBody.close();
}
Response response = readNetworkResponse();
int code = response.code();
if ((code == 204 || code == 205) && response.body().contentLength() > 0) {
throw new ProtocolException(
"HTTP " + code + " had non-zero Content-Length: " + response.body().contentLength());
}
return response;
}
}
主要來看下proceed方法
同樣還是一個迴圈遞迴呼叫,因此程式碼其實在最後一層還是會呼叫一下程式碼
具體每一行程式碼的作用在圖片中已經做了簡單介紹。我們重點來看一下最重要的讀取請求結果的方法–readNetworkResponse,程式碼如下:
到這我們就已經完成了從傳送網路請求到讀取請求結果的流程,簡單總結一下在HttpEngine中兩個方法的工作
sendRequest–查詢合適的Socket物件並封裝在HttpStream中
readResponse–通過HttpStream傳送請求,並讀取結果封裝到Response物件中返回