OKHttp原始碼解析(三)
阿新 • • 發佈:2019-01-03
public void readResponse() throws IOException {
if(this.userResponse == null) {
if(this.networkRequest == null && this.cacheResponse == null) {
throw new IllegalStateException("call sendRequest() first!");
} else if(this.networkRequest != null) {
Response networkResponse;
if (this.forWebSocket) {
this.transport.writeRequestHeaders(this.networkRequest);
networkResponse = this.readNetworkResponse();
} else if(!this.callerWritesRequestBody) {
//利用攔截器方式去做答覆處理
networkResponse = (new HttpEngine.NetworkInterceptorChain(0 , this.networkRequest)).proceed(this.networkRequest);
} else {
//這個else分句可以不看
}
this.receiveHeaders(networkResponse.headers());
if(this.cacheResponse != null) {
if(validate(this.cacheResponse, networkResponse)) {
this .userResponse = this.cacheResponse.newBuilder().request(this.userRequest).priorResponse(stripBody(this.priorResponse)).headers(combine(this.cacheResponse.headers(), networkResponse.headers())).cacheResponse(stripBody(this.cacheResponse)).networkResponse(stripBody(networkResponse)).build();
networkResponse.body().close();
this.releaseConnection();
InternalCache responseCache1 = Internal.instance.internalCache(this.client);
responseCache1.trackConditionalCacheHit();
responseCache1.update(this.cacheResponse, stripBody(this.userResponse));
this.userResponse = this.unzip(this.userResponse);
return;
}
Util.closeQuietly(this.cacheResponse.body());
}
this.userResponse = networkResponse.newBuilder().request(this.userRequest).priorResponse(stripBody(this.priorResponse)).cacheResponse(stripBody(this.cacheResponse)).networkResponse(stripBody(networkResponse)).build();
if(hasBody(this.userResponse)) {
this.maybeCache();
this.userResponse = this.unzip(this.cacheWritingResponse(this.storeRequest, this.userResponse));
}
}
}
}
在上篇文章結尾OKHttp原始碼解析(二) 我們分析到這裡第10行的callerWritesRequestBody會為空,所以我會直接進去這個判斷,同樣在OKHttp原始碼解析(一) 我們分析過傳送請求時使用的攔截器模式,這裡對答覆的操作也用了同樣的方式,不同於請求呼叫的是intercept,這裡用的是proceed,我們就來看看這個方法做了什麼
public Response proceed(Request request) throws IOException {
++this.calls;
if(this.index > 0) {
Interceptor response = (Interceptor)HttpEngine.this.client.networkInterceptors().get(this.index - 1);
Address code = this.connection().getRoute().getAddress();
if(!request.url().getHost().equals(code.getUriHost()) || Util.getEffectivePort(request.url()) != code.getUriPort()) {
throw new IllegalStateException("network interceptor " + response + " must retain the same host and port");
}
if(this.calls > 1) {
throw new IllegalStateException("network interceptor " + response + " must call proceed() exactly once");
}
}
if(this.index < HttpEngine.this.client.networkInterceptors().size()) {
//根據攔截器的數目去相應取出攔截器並執行intercept裡面使用者自定義的處理方式
HttpEngine.NetworkInterceptorChain var7 = HttpEngine.this.new NetworkInterceptorChain(this.index + 1, request);
Interceptor var10 = (Interceptor)HttpEngine.this.client.networkInterceptors().get(this.index);
Response interceptedResponse = var10.intercept(var7);
if(var7.calls != 1) {
throw new IllegalStateException("network interceptor " + var10 + " must call proceed() exactly once");
} else {
return interceptedResponse;
}
} else {
//寫入請求頭部
HttpEngine.this.transport.writeRequestHeaders(request);
HttpEngine.this.networkRequest = request;
if(HttpEngine.this.permitsRequestBody() && request.body() != null) {
//寫入一些請求體
Sink var5 = HttpEngine.this.transport.createRequestBody(request, request.body().contentLength());
BufferedSink var8 = Okio.buffer(var5);
request.body().writeTo(var8);
var8.close();
}
//將之前寫入的資料flush給socket並讀取伺服器答覆
Response var6 = HttpEngine.this.readNetworkResponse();
int var9 = var6.code();
if((var9 == 204 || var9 == 205) && var6.body().contentLength() > 0L) {
throw new ProtocolException("HTTP " + var9 + " had non-zero Content-Length: " + var6.body().contentLength());
} else {
return var6;
}
}
}
我們先來看看27行的頭部寫入是怎麼一個寫法
public void writeRequestHeaders(Request request) throws IOException {
this.httpEngine.writingRequestHeaders();
//組裝請求的資訊,比如url,請求方式,請求協議
String requestLine = RequestLine.get(request, this.httpEngine.getConnection().getRoute().getProxy().type(), this.httpEngine.getConnection().getProtocol());
//將請求頭和請求體寫入socket
this.httpConnection.writeRequest(request.headers(), requestLine);
}
public void writeRequest(Headers headers, String requestLine) throws IOException {
if(this.state != 0) {
throw new IllegalStateException("state: " + this.state);
} else {
this.sink.writeUtf8(requestLine).writeUtf8("\r\n");
int i = 0;
for(int size = headers.size(); i < size; ++i) {
this.sink.writeUtf8(headers.name(i)).writeUtf8(": ").writeUtf8(headers.value(i)).writeUtf8("\r\n");
}
this.sink.writeUtf8("\r\n");
this.state = 1;
}
}
上面的sink就是socket的寫入流,在上篇文章我們分析過怎麼得到它的,將請求頭部和請求體寫入socket後,proceed的第37行就要進行flush操作了
private Response readNetworkResponse() throws IOException {
//執行flush操作
this.transport.finishRequest();
//等待伺服器相應並讀取伺服器返回資訊組裝成我們需要的response
Response networkResponse = this.transport.readResponseHeaders().request(this.networkRequest).handshake(this.connection.getHandshake()).header(OkHeaders.SENT_MILLIS, Long.toString(this.sentRequestMillis)).header(OkHeaders.RECEIVED_MILLIS, Long.toString(System.currentTimeMillis())).build();
if(!this.forWebSocket) {
//組裝response的body
networkResponse = networkResponse.newBuilder().body(this.transport.openResponseBody(networkResponse)).build();
}
Internal.instance.setProtocol(this.connection, networkResponse.protocol());
return networkResponse;
}
先簡單看下第三行finishRequest呼叫的程式碼
public void finishRequest() throws IOException {
this.httpConnection.flush();
}
public void flush() throws IOException {
this.sink.flush();
}
再來看看第5行的組裝步驟
public Builder readResponseHeaders() throws IOException {
return this.httpConnection.readResponse();
}
public Builder readResponse() throws IOException {
if(this.state != 1 && this.state != 3) {
throw new IllegalStateException("state: " + this.state);
} else {
try {
StatusLine e;
Builder exception1;
do {
//從輸入流裡讀出答覆並組裝成答覆訊息
e = StatusLine.parse(this.source.readUtf8LineStrict());
exception1 = (new Builder()).protocol(e.protocol).code(e.code).message(e.message);
com.squareup.okhttp.Headers.Builder headersBuilder = new com.squareup.okhttp.Headers.Builder();
//答覆頭部的讀取
this.readHeaders(headersBuilder);
headersBuilder.add(OkHeaders.SELECTED_PROTOCOL, e.protocol.toString());
exception1.headers(headersBuilder.build());
} while(e.code == 100);
this.state = 4;
return exception1;
} catch (EOFException var4) {
IOException exception = new IOException("unexpected end of stream on " + this.connection + " (recycle count=" + Internal.instance.recycleCount(this.connection) + ")");
exception.initCause(var4);
throw exception;
}
}
}
上面的程式碼我們看到的是對答覆頭部的讀取整理,而readNetworkResponse()第8行則是對伺服器答覆的body進行整理組裝
public ResponseBody openResponseBody(Response response) throws IOException {
Source source = this.getTransferStream(response);
//最終返回一個輸入流
return new RealResponseBody(response.headers(), Okio.buffer(source));
}
private Source getTransferStream(Response response) throws IOException {
if(!HttpEngine.hasBody(response)) {
return this.httpConnection.newFixedLengthSource(0L);
} else if("chunked".equalsIgnoreCase(response.header("Transfer-Encoding"))) {
return this.httpConnection.newChunkedSource(this.httpEngine);
} else {
long contentLength = OkHeaders.contentLength(response);
return contentLength != -1L?this.httpConnection.newFixedLengthSource(contentLength):this.httpConnection.newUnknownLengthSource();
}
}
在經過這麼層層程式碼的深入(好多程式碼,看得都亂了吧),我們最終得到了伺服器返回的userResponse。。
最後的最後,我們要回到OKHttp原始碼解析(一) 最後一段程式碼getResponse的52行得到我們上面分析的userResponse,並關閉相應的連線池,關閉回收socket等”善後“處理。
三篇文章分析到現在,算是把整個OKHttp的流程分析了個大概,OkHttp還有其他的一些部分,比如handshake,連線池的管理等方面的內容,如果有人有發現這方面介紹的好文章可以留言推薦給我看看,大家共同學習,共同進步。