1. 程式人生 > >OkHttp面試之--HttpEngine中的readResponse流程簡介

OkHttp面試之--HttpEngine中的readResponse流程簡介

上一節主要大體看了一下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物件中返回