Okhttp解析—Interceptor詳解


Okhttp解析—Interceptor詳解

Interceptor可以說是okhttp的精髓之一,Okhttp重寫請求/響應、重試、緩存響應等操作,基本都是在各個Interceptor中完成的,上篇文章分析Okhttp運行流程時只是簡單帶過,那么這篇文章就來詳細分析下Interceptor以及攔截器鏈機制。

一、Interceptor以及InterceptorChain

/**
 * Observes, modifies, and potentially short-circuits requests going out and the corresponding
 * responses coming back in. Typically interceptors add, remove, or transform headers on the request
 * or response.
 */
public interface Interceptor {
  Response intercept(Chain chain) throws IOException;

  interface Chain {
    Request request();

    Response proceed(Request request) throws IOException;

    /**
     * Returns the connection the request will be executed on. This is only available in the chains
     * of network interceptors; for application interceptors this is always null.
     */
    @Nullable Connection connection();

    Call call();

    int connectTimeoutMillis();

    Chain withConnectTimeout(int timeout, TimeUnit unit);

    int readTimeoutMillis();

    Chain withReadTimeout(int timeout, TimeUnit unit);

    int writeTimeoutMillis();

    Chain withWriteTimeout(int timeout, TimeUnit unit);
  }
}

Interceptor最主要的就是其intercept()函數,InterceptorChain則是Interceptor一個內部類,它的主要作用就是鏈式有序調用Interceptor。

Okhttp內置了5種Interceptor它們分別是RetryAndFollowUpInterceptor
、BridgeInterceptor、CacheInterceptor、ConnectInterceptor
、CallServerInterceptor。正是這5種Interceptor完成了重寫、重試、緩存、請求等操作,接下來我們來逐一分析它們的作用。

二、Interceptor鏈式調用

攔截器調用的入口是在ReallCall的getResponseWithInterceptorChain()函數中。

Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    List<Interceptor> interceptors = new ArrayList<>();
    interceptors.addAll(client.interceptors());
    interceptors.add(new RetryAndFollowUpInterceptor(client));
    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));
    Interceptor.Chain chain = new RealInterceptorChain(interceptors, transmitter, null, 0,
        originalRequest, this, client.connectTimeoutMillis(),
        client.readTimeoutMillis(), client.writeTimeoutMillis());//1

    boolean calledNoMoreExchanges = false;
    try {
      Response response = chain.proceed(originalRequest);//2
      if (transmitter.isCanceled()) {
        closeQuietly(response);
        throw new IOException("Canceled");
      }
      return response;
    } catch (IOException e) {
      calledNoMoreExchanges = true;
      throw transmitter.noMoreExchanges(e);
    } finally {
      if (!calledNoMoreExchanges) {
        transmitter.noMoreExchanges(null);
      }
    }
  }

我們看到該函數一開始就構造了一個List然后向里邊添加所有的Interceptor,包括我們自定義的Application Interceptor、系統預置的Interceptor、自定義的Network Interceptor等。
然后在注釋1處構造RealInterceptorChain實例並傳入剛剛的interceptor list還有就是我們看到第四個參數此處傳入的是0。該參數表示接下來要調用interceptor list中哪個interceptor。
最后在注釋2處調用chain.proceed()並傳入原始請求。

@Override public Response proceed(Request request) throws IOException {
  return proceed(request, transmitter, exchange);
}

public Response proceed(Request request, Transmitter transmitter, @Nullable Exchange exchange)
    throws IOException {
  if (index >= interceptors.size()) throw new AssertionError();

  calls++;

  // If we already have a stream, confirm that the incoming request will use it.
  if (this.exchange != null && !this.exchange.connection().supportsUrl(request.url())) {
    throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
        + " must retain the same host and port");
  }

  // If we already have a stream, confirm that this is the only call to chain.proceed().
  if (this.exchange != null && calls > 1) {
    throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
        + " must call proceed() exactly once");
  }

  // Call the next interceptor in the chain.
  RealInterceptorChain next = new RealInterceptorChain(interceptors, transmitter, exchange,
      index + 1, request, call, connectTimeout, readTimeout, writeTimeout);//1
  Interceptor interceptor = interceptors.get(index);//2
  Response response = interceptor.intercept(next);//3

  // Confirm that the next interceptor made its required call to chain.proceed().
  if (exchange != null && index + 1 < interceptors.size() && next.calls != 1) {
    throw new IllegalStateException("network interceptor " + interceptor
        + " must call proceed() exactly once");
  }

  // Confirm that the intercepted response isn't null.
  if (response == null) {
    throw new NullPointerException("interceptor " + interceptor + " returned null");
  }

  if (response.body() == null) {
    throw new IllegalStateException(
        "interceptor " + interceptor + " returned a response with no body");
  }

  return response;
}

proceed函數我們主要關注3個注釋處的操作。
在注釋1處構造RealInterceptorChain,其中參數4此時變為index+1,index就是之前在getResponseWithInterceptorChain中構造RealInterceptorChain時傳入的值。

注釋2處通過index從Interceptor list中取出對應的Interceptor。
注釋3處調用Interceptor的intercept()方法,參數傳入注釋1處構造的RealInterceptorChain。因為Interceptor的操作都是在intercept()函數中完成的,所以該操作完成了當前Interceptor的調用。同時在每個Interceptor的intercept()函數中都會調用next.proceed()這樣就開啟了下一個Interceptor調用,如此反復最終像鏈條一樣依次調用Interceptor list中所有的Interceptor。

三、詳解各個Interceptor

上邊我們分析了Interceptor是按順序調用的,這里的順序指的是添加到Interceptor list中的先后。由getResponseWithInterceptorChain()方法可以調用的順序依次是(未考慮添加自定義Interceptor的情況):RetryAndFollowUpInterceptor-->BridgeInterceptor-->CacheInterceptor-->ConnectInterceptor-->CallServerInterceptor。

RetryAndFollowUpInterceptor

RetryAndFollowUpInterceptor的作用主要是重試與重定向。
來看下它的Interceptor方法。

@Override public Response intercept(Chain chain) throws IOException {
  Request request = chain.request();//獲取傳入的chain的request 此處的request是next的request
  RealInterceptorChain realChain = (RealInterceptorChain) chain;
  Transmitter transmitter = realChain.transmitter();

  int followUpCount = 0;
  Response priorResponse = null;
  while (true) {//開啟一個無限循環
    transmitter.prepareToConnect(request);

    if (transmitter.isCanceled()) {//如果此時請求被cancel拋出異常
      throw new IOException("Canceled");
    }

    Response response;
    boolean success = false;
    try {
      response = realChain.proceed(request, transmitter, null);//調用next的proceed方法,即調用下一個Interceptor
      success = true;
    } catch (RouteException e) {//如果通過某個route連接失敗則嘗試恢復。注意此時請求尚未發送出去
      // The attempt to connect via a route failed. The request will not have been sent.
      if (!recover(e.getLastConnectException(), transmitter, false, request)) {
        throw e.getFirstConnectException();
      }
      continue;
    } catch (IOException e) {//如果連接server失敗則嘗試恢復,注意此時請求已發送。
      // An attempt to communicate with a server failed. The request may have been sent.
      boolean requestSendStarted = !(e instanceof ConnectionShutdownException);
      if (!recover(e, transmitter, requestSendStarted, request)) throw e;
      continue;
    } finally {
      // The network call threw an exception. Release any resources.
      if (!success) {//執行不成功關閉
        transmitter.exchangeDoneDueToException();
      }
    }

    // Attach the prior response if it exists. Such responses never have a body.
    if (priorResponse != null) {//priorResponse不為空表示之前發生過重定向,此時為本次的response設置priorResponse
      response = response.newBuilder()
          .priorResponse(priorResponse.newBuilder()
                  .body(null)
                  .build())
          .build();
    }

    Exchange exchange = Internal.instance.exchange(response);
    Route route = exchange != null ? exchange.connection().route() : null;
    Request followUp = followUpRequest(response, route);//生成重定向的請求

    if (followUp == null) {//followUp == null說明無需重定向,返回當前respone
      if (exchange != null && exchange.isDuplex()) {
        transmitter.timeoutEarlyExit();
      }
      return response;
    }

    RequestBody followUpBody = followUp.body();
    if (followUpBody != null && followUpBody.isOneShot()) {
      return response;
    }

    closeQuietly(response.body());
    if (transmitter.hasExchange()) {
      exchange.detachWithViolence();
    }

    if (++followUpCount > MAX_FOLLOW_UPS) {//判斷是否超過最大重定向次數
      throw new ProtocolException("Too many follow-up requests: " + followUpCount);
    }

    request = followUp;
    priorResponse = response;
  }
}

來,梳理下整個流程:

  1. 開啟一個無限循環,直至沒有重定向或有異常拋出才會結束。
  2. 處理cancel
  3. 調用下一個Interceptor獲取respone
  4. 如果步驟3發生異常,嘗試恢復並重試請求
  5. 如果步驟3未發生異常為,priorResponse不為空(如果priorResponse表示之前發生過重定向),為當前respone設置priorResponse(注意priorResponse body是null)
  6. 調用followUpRequest根據返回的code生成用於重定向的請求
  7. 步驟6生成的請求為空表示無需重定向,返回當前respone,結束循環
  8. 步驟6生成的請求不為空表示需要重定向,此時重定向次數+1,然后判斷是否超過最大重定向次數,未超過則跳轉到步驟2開始下次循環。

步驟3是攔截器能鏈式有序調用的關鍵。
步驟4的判斷是否能恢復是通過recover()函數,具體看注釋

/**
 * Report and attempt to recover from a failure to communicate with a server. Returns true if
 * {@code e} is recoverable, or false if the failure is permanent. Requests with a body can only
 * be recovered if the body is buffered or if the failure occurred before the request has been
 * sent.
 */
private boolean recover(IOException e, Transmitter transmitter,
    boolean requestSendStarted, Request userRequest) {
  // The application layer has forbidden retries.
//應用設置禁止重試 返回false
  if (!client.retryOnConnectionFailure()) return false;

  // We can't send the request body again.
//request設置僅能執行一次 返回false
  if (requestSendStarted && requestIsOneShot(e, userRequest)) return false;

  // This exception is fatal.
//異常是致命的 返回false
  if (!isRecoverable(e, requestSendStarted)) return false;

  // No more routes to attempt.
//沒有更多的route可供嘗試 返回fasle
  if (!transmitter.canRetry()) return false;

  // For failure recovery, use the same route selector with a new connection.
  return true;
}

可以看出恢復操作是有條件的,在某些條件下是不能恢復的。另外上面說的不可恢復致命異常有ProtocolException、InterruptedIOException、SSLHandshakeException、CertificateException
、SSLPeerUnverifiedException。

步驟6的followUpRequest可以說是重定向的核心了,看下followUpRequest函數

private Request followUpRequest(Response userResponse, @Nullable Route route) throws IOException {
  if (userResponse == null) throw new IllegalStateException();
  int responseCode = userResponse.code();//獲取當前respone的返回code

  final String method = userResponse.request().method();
  switch (responseCode) {//根據responseCode的值分情況處理
    case HTTP_PROXY_AUTH://407
      Proxy selectedProxy = route != null
          ? route.proxy()
          : client.proxy();
      if (selectedProxy.type() != Proxy.Type.HTTP) {
        throw new ProtocolException("Received HTTP_PROXY_AUTH (407) code while not using proxy");
      }
      return client.proxyAuthenticator().authenticate(route, userResponse);//代理驗證

    case HTTP_UNAUTHORIZED://401
      return client.authenticator().authenticate(route, userResponse);//身份認證

    case HTTP_PERM_REDIRECT:
    case HTTP_TEMP_REDIRECT:
      // "If the 307 or 308 status code is received in response to a request other than GET
      // or HEAD, the user agent MUST NOT automatically redirect the request"
      if (!method.equals("GET") && !method.equals("HEAD")) {//307、308 兩種code不對 GET、HEAD 以外的請求重定向
        return null;
      }
      // fall-through
    case HTTP_MULT_CHOICE://300
    case HTTP_MOVED_PERM://301
    case HTTP_MOVED_TEMP://302
    case HTTP_SEE_OTHER://303 
//以上這四種code是可以進行重定向的
      // Does the client allow redirects?
      if (!client.followRedirects()) return null;//客戶端不允許重定向 返回null

      String location = userResponse.header("Location");//獲取Location以確定重定向目標
      if (location == null) return null;//Response的Location為null 返回null
      HttpUrl url = userResponse.request().url().resolve(location);

      // Don't follow redirects to unsupported protocols.
      if (url == null) return null;

      // If configured, don't follow redirects between SSL and non-SSL.
      boolean sameScheme = url.scheme().equals(userResponse.request().url().scheme());
      if (!sameScheme && !client.followSslRedirects()) return null;

      // Most redirects don't include a request body.
      Request.Builder requestBuilder = userResponse.request().newBuilder();
      if (HttpMethod.permitsRequestBody(method)) {
        final boolean maintainBody = HttpMethod.redirectsWithBody(method);//是否帶body重定向
        if (HttpMethod.redirectsToGet(method)) {
          requestBuilder.method("GET", null);
        } else {
          RequestBody requestBody = maintainBody ? userResponse.request().body() : null;
          requestBuilder.method(method, requestBody);
        }
        if (!maintainBody) {
          requestBuilder.removeHeader("Transfer-Encoding");
          requestBuilder.removeHeader("Content-Length");
          requestBuilder.removeHeader("Content-Type");
        }
      }

      // When redirecting across hosts, drop all authentication headers. This
      // is potentially annoying to the application layer since they have no
      // way to retain them.
      if (!sameConnection(userResponse.request().url(), url)) {
        requestBuilder.removeHeader("Authorization");
      }

      return requestBuilder.url(url).build();//返回構造的重定向request

    case HTTP_CLIENT_TIMEOUT://408 實際很少用到,一般需要重復發送一個相同的請求
      // 408's are rare in practice, but some servers like HAProxy use this response code. The
      // spec says that we may repeat the request without modifications. Modern browsers also
      // repeat the request (even non-idempotent ones.)
      if (!client.retryOnConnectionFailure()) {
        // The application layer has directed us not to retry the request.
        return null;
      }

      RequestBody requestBody = userResponse.request().body();
      if (requestBody != null && requestBody.isOneShot()) {
        return null;
      }

      if (userResponse.priorResponse() != null
          && userResponse.priorResponse().code() == HTTP_CLIENT_TIMEOUT) {
        // We attempted to retry and got another timeout. Give up.
        return null;
      }

      if (retryAfter(userResponse, 0) > 0) {
        return null;
      }

      return userResponse.request();

    case HTTP_UNAVAILABLE://503
      if (userResponse.priorResponse() != null
          && userResponse.priorResponse().code() == HTTP_UNAVAILABLE) {
        // We attempted to retry and got another timeout. Give up.
        return null;
      }

      if (retryAfter(userResponse, Integer.MAX_VALUE) == 0) {
        // specifically received an instruction to retry without delay
        return userResponse.request();
      }

      return null;

    default:
      return null;
  }
}

followUpRequest主要是根據返回的code分情況處理,如果允許重定向則返回新構造的request否則返回null表示不允許重定向。

BridgeInterceptor

BridgeInterceptor名為橋接攔截器主要作用就是把我們傳入的request“重寫”為實際向server請求的request,收到respone后“重寫”respone。來看下其intercept函數

@Override public Response intercept(Chain chain) throws IOException {
  Request userRequest = chain.request();
  Request.Builder requestBuilder = userRequest.newBuilder();

  RequestBody body = userRequest.body();//獲取請求body
  if (body != null) {//請求發送前“重寫”headers
    MediaType contentType = body.contentType();
    if (contentType != null) {
      requestBuilder.header("Content-Type", contentType.toString());
    }

    long contentLength = body.contentLength();
    if (contentLength != -1) {
      requestBuilder.header("Content-Length", Long.toString(contentLength));
      requestBuilder.removeHeader("Transfer-Encoding");
    } else {
      requestBuilder.header("Transfer-Encoding", "chunked");
      requestBuilder.removeHeader("Content-Length");
    }
  }

  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");
  }

  List<Cookie> cookies = cookieJar.loadForRequest(userRequest.url());
  if (!cookies.isEmpty()) {//cookie不為空則添加cookie
    requestBuilder.header("Cookie", cookieHeader(cookies));
  }

  if (userRequest.header("User-Agent") == null) {//設置User-Agent
    requestBuilder.header("User-Agent", Version.userAgent());
  }

  Response networkResponse = chain.proceed(requestBuilder.build());//開啟下一個攔截器的調用

  HttpHeaders.receiveHeaders(cookieJar, userRequest.url(), networkResponse.headers());//從這里開始是收到respone然后對其“重寫”

  Response.Builder responseBuilder = networkResponse.newBuilder()
      .request(userRequest);

  if (transparentGzip
      && "gzip".equalsIgnoreCase(networkResponse.header("Content-Encoding"))
      && HttpHeaders.hasBody(networkResponse)) {//如果之前采用gzip進行壓縮,那么需要對respone進行解壓
    GzipSource responseBody = new GzipSource(networkResponse.body().source());
    Headers strippedHeaders = networkResponse.headers().newBuilder()
        .removeAll("Content-Encoding")
        .removeAll("Content-Length")
        .build();
    responseBuilder.headers(strippedHeaders);
    String contentType = networkResponse.header("Content-Type");
    responseBuilder.body(new RealResponseBody(contentType, -1L, Okio.buffer(responseBody)));
  }

  return responseBuilder.build();
}

BridgeInterceptor完成的工作可以分為3步:

  1. 請求發送前對request“重寫”,重寫后的request才是實際去用來請求的request。
  2. 調用chain.proceed開啟下一個攔截器調用,並拿到respone
  3. 對返回的respone進行“重寫”,我們拿到的respone就是重寫后的

對request和respone的“重寫”基本都是針對其headers,比如發送請求前未設置Accept-EncodingOkhttp會為你設置,在有cookie的情況下為你添加cookie,在拿到respone后如果需要解壓縮Okhttp會為你自動解壓縮。

CacheInterceptor

CacheInterceptor是緩存攔截器,主要作用是定義Okhttp的緩存機制。

@Override public Response intercept(Chain chain) throws IOException {
  Response cacheCandidate = cache != null
      ? cache.get(chain.request())
      : null;//若當前有cache則根據請求獲取對應的緩存

  long now = System.currentTimeMillis();

  //構造緩存策略
  CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();
  Request networkRequest = strategy.networkRequest;
  Response cacheResponse = strategy.cacheResponse;

  if (cache != null) {
    cache.trackResponse(strategy);
  }

  if (cacheCandidate != null && cacheResponse == null) {//有緩存但不可用關閉
    closeQuietly(cacheCandidate.body()); // The cache candidate wasn't applicable. Close it.
  }

  // If we're forbidden from using the network and the cache is insufficient, fail.

  //如果設置禁止從網絡獲取響應且緩存不可用那么返回失敗
  if (networkRequest == null && cacheResponse == null) {
    return new Response.Builder()
        .request(chain.request())
        .protocol(Protocol.HTTP_1_1)
        .code(504)
        .message("Unsatisfiable Request (only-if-cached)")
        .body(Util.EMPTY_RESPONSE)
        .sentRequestAtMillis(-1L)
        .receivedResponseAtMillis(System.currentTimeMillis())
        .build();
  }

  // If we don't need the network, we're done.
  //從cache獲取respone不使用網絡
  if (networkRequest == null) {
    return cacheResponse.newBuilder()
        .cacheResponse(stripBody(cacheResponse))
        .build();
  }

  Response networkResponse = null;
  try {
    networkResponse = chain.proceed(networkRequest);//調用下一個攔截器,從網絡獲取respone
  } finally {
    // If we're crashing on I/O or otherwise, don't leak the cache body.
    if (networkResponse == null && cacheCandidate != null) {
      closeQuietly(cacheCandidate.body());
    }
  }

  // If we have a cache response too, then we're doing a conditional get.
  if (cacheResponse != null) {
    if (networkResponse.code() == HTTP_NOT_MODIFIED) {//從網絡獲取respone且緩存非空 如果返回碼為304 則更新緩存然后返回
      Response response = cacheResponse.newBuilder()
          .headers(combine(cacheResponse.headers(), networkResponse.headers()))
          .sentRequestAtMillis(networkResponse.sentRequestAtMillis())
          .receivedResponseAtMillis(networkResponse.receivedResponseAtMillis())
          .cacheResponse(stripBody(cacheResponse))
          .networkResponse(stripBody(networkResponse))
          .build();
      networkResponse.body().close();

      // Update the cache after combining headers but before stripping the
      // Content-Encoding header (as performed by initContentStream()).
      cache.trackConditionalCacheHit();
      cache.update(cacheResponse, response);
      return response;
    } else {
      closeQuietly(cacheResponse.body());
    }
  }

  //沒有緩存可供使用,讀取網絡響應構造respone
  Response response = networkResponse.newBuilder()
      .cacheResponse(stripBody(cacheResponse))
      .networkResponse(stripBody(networkResponse))
      .build();

  if (cache != null) {//cache不為空 把respone緩存到cache
    if (HttpHeaders.hasBody(response) && CacheStrategy.isCacheable(response, networkRequest)) {
      // Offer this request to the cache.
      CacheRequest cacheRequest = cache.put(response);
      return cacheWritingResponse(cacheRequest, response);
    }

    if (HttpMethod.invalidatesCache(networkRequest.method())) {
      try {
        cache.remove(networkRequest);
      } catch (IOException ignored) {
        // The cache cannot be written.
      }
    }
  }

  return response;
}

整個流程如下:

  1. 判斷是否有Cache,有的話根據request去嘗試獲取緩存
  2. 構造緩存策略
  3. 步驟1獲取的緩存respone不為空但是不可用,關閉連接
  4. 設置禁止從網絡獲取響應且緩存不可用那么返回504失敗
  5. 從cache獲取respone 不使用網絡(步驟1-5的作用就是在有請求時先嘗試從本地獲取緩存如果失敗才會去從網絡獲取否則返回緩存)
  6. 調用下一個攔截器,從網絡獲取respone
  7. 從網絡獲取respone且緩存非空 如果返回碼為304 則更新緩存然后返回
  8. 沒有緩存可供使用,讀取網絡響應構造respone
  9. cache不為空 把respone緩存到cache(步驟6-9作用就是從網絡獲取respone然后把獲得的respone緩存到本地)

CacheInterceptor中涉及兩個類:Cache、CacheStrategy,這里先不展開分析,等分析Okhttp緩存機制時再做詳細介紹。

ConnectInterceptor

ConnectInterceptor是連接攔截器,它的intercept函數非常簡潔:

@Override public Response intercept(Chain chain) throws IOException {
  RealInterceptorChain realChain = (RealInterceptorChain) chain;
  Request request = realChain.request();
  Transmitter transmitter = realChain.transmitter();//獲取Transmitter
  // We need the network to satisfy this request. Possibly for validating a conditional GET.
  boolean doExtensiveHealthChecks = !request.method().equals("GET");
  Exchange exchange = transmitter.newExchange(chain, doExtensiveHealthChecks);//構造Exchange

  return realChain.proceed(request, transmitter, exchange);//開啟下一個攔截器調用並傳入Transmitter、Exchange。
}

它首先獲取Transmitter,然后通過Transmitter的newExchange方法創建一個Exchange,把它傳到下一個攔截器。
Transmitter是應用和網絡之間的一個橋梁,通過transmitter.newExchange構造一個Exchange實例

Exchange newExchange(Interceptor.Chain chain, boolean doExtensiveHealthChecks) {
  synchronized (connectionPool) {
    if (noMoreExchanges) {
      throw new IllegalStateException("released");
    }
    if (exchange != null) {
      throw new IllegalStateException("cannot make a new request because the previous response "
          + "is still open: please call response.close()");
    }
  }

  ExchangeCodec codec = exchangeFinder.find(client, chain, doExtensiveHealthChecks);
  Exchange result = new Exchange(this, call, eventListener, exchangeFinder, codec);

  synchronized (connectionPool) {
    this.exchange = result;
    this.exchangeRequestDone = false;
    this.exchangeResponseDone = false;
    return result;
  }
}

newExchange主要做了兩件事:調用ExchangeFinder.find獲取一個ExchangeCodec、構造一個Exchange。
ExchangeFinder就是負責連接的創建,把創建好的連接放入連接池,如果連接池中已經有該連接,就直接取出復用。而ExchangeCodec則是對HTTP請求和HTTP響應編碼
Exchange則是用來進行發送和接收HTTP request和respone。

CallServerInterceptor

CallServerInterceptor是攔截器鏈中最后一個攔截器,與服務器的交互如,發出請求和接收響應都是它完成的。

@Override public Response intercept(Chain chain) throws IOException {
  RealInterceptorChain realChain = (RealInterceptorChain) chain;
  Exchange exchange = realChain.exchange();
  Request request = realChain.request();

  long sentRequestMillis = System.currentTimeMillis();

  exchange.writeRequestHeaders(request);//向服務端寫請求

  boolean responseHeadersStarted = false;
  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"))) {
      exchange.flushRequest();
      responseHeadersStarted = true;
      exchange.responseHeadersStart();
      responseBuilder = exchange.readResponseHeaders(true);
    }

    if (responseBuilder == null) {
      if (request.body().isDuplex()) {
        // Prepare a duplex body so that the application can send a request body later.
        exchange.flushRequest();
        BufferedSink bufferedRequestBody = Okio.buffer(
            exchange.createRequestBody(request, true));
        request.body().writeTo(bufferedRequestBody);
      } else {
        // Write the request body if the "Expect: 100-continue" expectation was met.
        BufferedSink bufferedRequestBody = Okio.buffer(
            exchange.createRequestBody(request, false));
        request.body().writeTo(bufferedRequestBody);
        bufferedRequestBody.close();
      }
    } else {
      exchange.noRequestBody();
      if (!exchange.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.
        exchange.noNewExchangesOnConnection();
      }
    }
  } else {
    exchange.noRequestBody();
  }

  if (request.body() == null || !request.body().isDuplex()) {
    exchange.finishRequest();
  }

  if (!responseHeadersStarted) {
    exchange.responseHeadersStart();//從服務端獲取請求
  }

  if (responseBuilder == null) {
    responseBuilder = exchange.readResponseHeaders(false);
  }

  Response response = responseBuilder
      .request(request)
      .handshake(exchange.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
    response = exchange.readResponseHeaders(false)
        .request(request)
        .handshake(exchange.connection().handshake())
        .sentRequestAtMillis(sentRequestMillis)
        .receivedResponseAtMillis(System.currentTimeMillis())
        .build();

    code = response.code();
  }

  exchange.responseHeadersEnd(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(exchange.openResponseBody(response))
        .build();
  }

  if ("close".equalsIgnoreCase(response.request().header("Connection"))
      || "close".equalsIgnoreCase(response.header("Connection"))) {
    exchange.noNewExchangesOnConnection();
  }

  if ((code == 204 || code == 205) && response.body().contentLength() > 0) {
    throw new ProtocolException(
        "HTTP " + code + " had non-zero Content-Length: " + response.body().contentLength());
  }

  return response;
}

在ConnectInterceptor中我們已經與服務器建立了連接,獲取了輸入輸出流,所以CallServerInterceptor的intercept(Chain)方法邏輯就是把請求發送到服務器,然后獲取服務器的響應。

發送請求

通過Exchange的writeRequestHeaders(request)方法寫入請求的header;如果請求的body不為空,通過okio寫入請求的body。

獲取響應

通過Exchange的readResponseHeaders(boolean)方法讀取響應的header;通過Exchange的openResponseBody(Response)方法讀取響應的body。

可以看出發送請求個獲取響應都是通過exchange來進行的。

至此Okhttp的攔截器機制我們就分析完了,以上是Okhttp已經定義好的攔截器,在實際的使用中我們可以自定義攔截器來完成我們想要的功能。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM