1. 程式人生 > >OKHttp原始碼解析(三)

OKHttp原始碼解析(三)

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,連線池的管理等方面的內容,如果有人有發現這方面介紹的好文章可以留言推薦給我看看,大家共同學習,共同進步。