Android源碼學(xué)習(xí)-OkHttp源碼淺析

介紹

OkHttp是一個(gè)用于Android和Java應(yīng)用程序的HTTP客戶端。相關(guān)參數(shù)和配置都是用Builder模式構(gòu)建,簡(jiǎn)單使用如下。

OkHttpClient httpClient = new OkHttpClient.Builder().build();
Request request = new Request.Builder().get().url("http://www.baidu.com").build();
Call call = httpClient.newCall(request);
call.enqueue(new Callback() {
    @Override
    public void onFailure(Call call, IOException e) {

    }

    @Override
    public void onResponse(Call call, Response response) throws IOException {

    }
});

上面是異步請(qǐng)求方式,而同步請(qǐng)求方式只需要替換call調(diào)用的方法即可:

call.execute();

當(dāng)OkHttp發(fā)送同步請(qǐng)求后,就會(huì)進(jìn)入阻塞狀態(tài),直到收到響應(yīng)。而異步請(qǐng)求則是異步執(zhí)行的。

源碼分析

基于OkHttp3.14.0

OkHttpClient的構(gòu)建

OkHttpClient是OkHttp客戶端類(lèi),使用Builder模式構(gòu)建,初始化OkHttpClient靜態(tài)內(nèi)部類(lèi)Builder,并調(diào)用器build方法即可完成OkHttpClient的構(gòu)建。

public Builder() {
  dispatcher = new Dispatcher();
  protocols = DEFAULT_PROTOCOLS;
  connectionSpecs = DEFAULT_CONNECTION_SPECS;
  eventListenerFactory = EventListener.factory(EventListener.NONE);
  proxySelector = ProxySelector.getDefault();
  if (proxySelector == null) {
    proxySelector = new NullProxySelector();
  }
  cookieJar = CookieJar.NO_COOKIES;
  socketFactory = SocketFactory.getDefault();
  hostnameVerifier = OkHostnameVerifier.INSTANCE;
  certificatePinner = CertificatePinner.DEFAULT;
  proxyAuthenticator = Authenticator.NONE;
  authenticator = Authenticator.NONE;
  connectionPool = new ConnectionPool();
  dns = Dns.SYSTEM;
  followSslRedirects = true;
  followRedirects = true;
  retryOnConnectionFailure = true;
  callTimeout = 0;
  connectTimeout = 10_000;
  readTimeout = 10_000;
  writeTimeout = 10_000;
  pingInterval = 0;
}

在Builder構(gòu)造方法中進(jìn)行相關(guān)參數(shù)的初始化賦值, 其中dispatcher分發(fā)器是OkHttp中很重要的概念,負(fù)責(zé)調(diào)度和管理異步請(qǐng)求隊(duì)列,決定異步請(qǐng)求何時(shí)執(zhí)行的策略。

public OkHttpClient build() {
  return new OkHttpClient(this);
}

buid方法將構(gòu)建的Builder對(duì)象傳入到OkHttpClient的構(gòu)造方法并返回OkHttpClient對(duì)象。

OkHttpClient(Builder builder) {
  this.dispatcher = builder.dispatcher;
  this.proxy = builder.proxy;
  this.protocols = builder.protocols;
  this.connectionSpecs = builder.connectionSpecs;
  this.interceptors = Util.immutableList(builder.interceptors);
  this.networkInterceptors = Util.immutableList(builder.networkInterceptors);
  this.eventListenerFactory = builder.eventListenerFactory;
  this.proxySelector = builder.proxySelector;
  this.cookieJar = builder.cookieJar;
  this.cache = builder.cache;
  this.internalCache = builder.internalCache;
  this.socketFactory = builder.socketFactory;

  boolean isTLS = false;
  for (ConnectionSpec spec : connectionSpecs) {
    isTLS = isTLS || spec.isTls();
  }

  if (builder.sslSocketFactory != null || !isTLS) {
    this.sslSocketFactory = builder.sslSocketFactory;
    this.certificateChainCleaner = builder.certificateChainCleaner;
  } else {
    X509TrustManager trustManager = Util.platformTrustManager();
    this.sslSocketFactory = newSslSocketFactory(trustManager);
    this.certificateChainCleaner = CertificateChainCleaner.get(trustManager);
  }

  if (sslSocketFactory != null) {
    Platform.get().configureSslSocketFactory(sslSocketFactory);
  }
  this.hostnameVerifier = builder.hostnameVerifier;
  this.certificatePinner = builder.certificatePinner.withCertificateChainCleaner(
      certificateChainCleaner);
  //...
  this.connectionPool = builder.connectionPool;
  //...省略部分初始化復(fù)制代碼
}

Request的構(gòu)建

Request主要是OkHttp請(qǐng)求參數(shù)的封裝,也是使用Builder模式構(gòu)建,初始化Request靜態(tài)內(nèi)部類(lèi)Builder,并調(diào)用其build方法完成Request的構(gòu)建。

public Builder() {
  this.method = "GET";
  this.headers = new Headers.Builder();
}

這里默認(rèn)請(qǐng)求方法設(shè)置為GET方法,并且初始化headers請(qǐng)求頭參數(shù)。

Builder可以設(shè)置網(wǎng)絡(luò)請(qǐng)求的方法,url等參數(shù)

public Builder get() {
  return method("GET", null);
}
public Builder url(String url) {
  if (url == null) throw new NullPointerException("url == null");

  // Silently replace web socket URLs with HTTP URLs.
  if (url.regionMatches(true, 0, "ws:", 0, 3)) {
    url = "http:" + url.substring(3);
  } else if (url.regionMatches(true, 0, "wss:", 0, 4)) {
    url = "https:" + url.substring(4);
  }

  return url(HttpUrl.get(url));
}

調(diào)用build方法,完成Request的創(chuàng)建。

public Request build() {
  if (url == null) throw new IllegalStateException("url == null");
  return new Request(this);
}
Request(Builder builder) {
  this.url = builder.url;
  this.method = builder.method;
  this.headers = builder.headers.build();
  this.body = builder.body;
  this.tags = Util.immutableMap(builder.tags);
}

如上,Request主要封裝了url,method,headers和請(qǐng)求體body。

Call的創(chuàng)建

Call是一個(gè)接口,其實(shí)現(xiàn)類(lèi)是RealCall。這個(gè)Call就是準(zhǔn)備好被執(zhí)行的一個(gè)請(qǐng)求,它可以被取消,但是不能被執(zhí)行兩次,因?yàn)檫@個(gè)對(duì)象代表的一個(gè)單獨(dú)的請(qǐng)求響應(yīng)對(duì)。

將request對(duì)象傳遞到OkHttpClient對(duì)象的newCall方法中進(jìn)行Call對(duì)象的創(chuàng)建

@Override public Call newCall(Request request) {
  return RealCall.newRealCall(this, request, false /* for web socket */);
}

調(diào)用RealCall的newRealCall方法

static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
  // Safely publish the Call instance to the EventListener.
  RealCall call = new RealCall(client, originalRequest, forWebSocket);
  call.eventListener = client.eventListenerFactory().create(call);
  return call;
}

該方法中直接實(shí)例化了一個(gè)RealCall對(duì)象并返回

private RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
  this.client = client;
  this.originalRequest = originalRequest;
  this.forWebSocket = forWebSocket;
  this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);
  this.timeout = new AsyncTimeout() {
    @Override protected void timedOut() {
      cancel();
    }
  };
  this.timeout.timeout(client.callTimeoutMillis(), MILLISECONDS);
}

retryAndFollowUpInterceptor重定向和重試攔截器是一個(gè)攔截器對(duì)象,這也是OkHttp中一個(gè)重要的概念,而OkHttp就是通過(guò)執(zhí)行攔截器鏈中的各個(gè)攔截器完成的網(wǎng)絡(luò)請(qǐng)求。

調(diào)用enqueue方法進(jìn)行異步網(wǎng)絡(luò)請(qǐng)求

經(jīng)過(guò)前幾步,客戶端類(lèi)、參數(shù)類(lèi),以及請(qǐng)求類(lèi)RealCall已經(jīng)完成創(chuàng)建,下面就是調(diào)用enqueue發(fā)起網(wǎng)絡(luò)請(qǐng)求并得到回調(diào)結(jié)果了。

@Override public void enqueue(Callback responseCallback) {
  synchronized (this) {
    if (executed) throw new IllegalStateException("Already Executed");
    executed = true;
  }
  //...
  eventListener.callStart(this);
  client.dispatcher().enqueue(new AsyncCall(responseCallback));
}

由于Call只能執(zhí)行一次,如果判斷executed標(biāo)志位為true,則拋出異常,否則將executed置為true。

將傳遞進(jìn)來(lái)的responseCallback對(duì)象封裝成AsyncCall,而AsyncCall繼承自NamedRunnable,它是一個(gè)Runnable對(duì)象。AsyncCall作為參數(shù)傳遞到dispatcher的enqueue方法中

void enqueue(AsyncCall call) {
  synchronized (this) {
    readyAsyncCalls.add(call);
  }
  promoteAndExecute();
}

將call對(duì)象傳遞到readyAsyncCalls隊(duì)列,這是一個(gè)等待執(zhí)行的Call隊(duì)列。調(diào)用promoteAndExecute方法

private boolean promoteAndExecute() {
  assert (!Thread.holdsLock(this));

  List<AsyncCall> executableCalls = new ArrayList<>();
  boolean isRunning;
  synchronized (this) {
    for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
      AsyncCall asyncCall = i.next();

      if (runningAsyncCalls.size() >= maxRequests) break; // Max capacity.
      if (runningCallsForHost(asyncCall) >= maxRequestsPerHost) continue; // Host max capacity.

      i.remove();
      executableCalls.add(asyncCall);
      runningAsyncCalls.add(asyncCall);
    }
    isRunning = runningCallsCount() > 0;
  }

  for (int i = 0, size = executableCalls.size(); i < size; i++) {
    AsyncCall asyncCall = executableCalls.get(i);
    asyncCall.executeOn(executorService());
  }

  return isRunning;

dispatcher中維護(hù)這兩個(gè)隊(duì)列,一個(gè)是剛看到的等待執(zhí)行call隊(duì)列readyAsyncCalls,還有一個(gè)是正在執(zhí)行call隊(duì)列runningAsyncCalls(包括正在執(zhí)行以及取消了的call)。而前面enqueue方法中,我們都將call添加到了readyAsyncCalls隊(duì)列中,所以該方法的作用是將符合條件的call從readyAsyncCalls中移除添加到runningAsyncCalls,并使用線程池執(zhí)行這些call請(qǐng)求。

首先遍歷readyAsyncCalls隊(duì)列,判斷如果當(dāng)前runningAsyncCalls隊(duì)列數(shù)量大于最大請(qǐng)求數(shù)64個(gè),直接break跳出;如果call請(qǐng)求的host存在的請(qǐng)求數(shù)大于5,則進(jìn)行下一個(gè)循環(huán)。如果上面的兩個(gè)條件都過(guò)了,就將當(dāng)前call從readyAsyncCalls中移除,并將其添加到executableCalls和runningAsyncCalls隊(duì)列中。最后遍歷executableCalls隊(duì)列,調(diào)用asyncCall的executeOn方法,在線程池中執(zhí)行請(qǐng)求

void executeOn(ExecutorService executorService) {
  assert (!Thread.holdsLock(client.dispatcher()));
  boolean success = false;
  try {
    executorService.execute(this);
    success = true;
  } catch (RejectedExecutionException e) {
    InterruptedIOException ioException = new InterruptedIOException("executor rejected");
    ioException.initCause(e);
    eventListener.callFailed(RealCall.this, ioException);
    responseCallback.onFailure(RealCall.this, ioException);
  } finally {
    if (!success) {
      client.dispatcher().finished(this); // This call is no longer running!
    }
  }
}

該方法中,主要執(zhí)行了三個(gè)操作:

1.這個(gè)try塊中執(zhí)行當(dāng)前AsyncCall

2.catch塊進(jìn)行了請(qǐng)求錯(cuò)誤回調(diào)

3.如果未執(zhí)行成功,在finally中將當(dāng)前AsyncCall從runningAsyncCalls中移除

主要看一下第一個(gè)操作。AsyncCall繼承了NamedRunnable對(duì)象,而在NamedRunnable的run方法中調(diào)用了execute抽象方法,所以主要執(zhí)行邏輯由AsyncCall的execute完成。

 @Override protected void execute() {
    boolean signalledCallback = false;
    timeout.enter();
    try {
      Response response = getResponseWithInterceptorChain();
      if (retryAndFollowUpInterceptor.isCanceled()) {
        signalledCallback = true;
        responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
      } else {
        signalledCallback = true;
        responseCallback.onResponse(RealCall.this, response);
      }
    } catch (IOException e) {
      e = timeoutExit(e);
      if (signalledCallback) {
        // Do not signal the callback twice!
        Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);
      } else {
        eventListener.callFailed(RealCall.this, e);
        responseCallback.onFailure(RealCall.this, e);
      }
    } finally {
      client.dispatcher().finished(this);
    }
  }
}

該方法執(zhí)行的操作:

1.try塊中調(diào)用getResponseWithInterceptorChain方法依次執(zhí)行攔截器鏈中的攔截器獲得response結(jié)果,判斷retryAndFollowUpInterceptor攔截器如果是取消狀態(tài),直接進(jìn)行失敗回調(diào),否則進(jìn)行成功回調(diào)。

2.catch塊也進(jìn)行了相應(yīng)的錯(cuò)誤回調(diào)。

3.同樣在finally塊中,不管當(dāng)前請(qǐng)求執(zhí)行成功與否都移除了當(dāng)前AsyncCall對(duì)象。

到這里就完成了OkHttp網(wǎng)絡(luò)請(qǐng)求的全部操作,獲取了請(qǐng)求成功的響應(yīng)并進(jìn)行了成功回調(diào),如果出錯(cuò)了,也進(jìn)行了相應(yīng)的失敗回調(diào)。

OkHttp攔截器鏈的執(zhí)行

OkHttp網(wǎng)絡(luò)請(qǐng)求的操作主要就是依次調(diào)用并執(zhí)行攔截器鏈中的攔截器完成的。

getResponseWithInterceptorChain()就是這個(gè)攔截器鏈調(diào)用流程的起點(diǎn):

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

  return chain.proceed(originalRequest);
}

這里將攔截器依次添加到攔截器集合中,接下來(lái)初始化了攔截器鏈對(duì)象,這個(gè)攔截器鏈持有了所有的應(yīng)用攔截器,網(wǎng)絡(luò)攔截器,OkHttp核心以及網(wǎng)絡(luò)調(diào)用者,最后調(diào)用了攔截器鏈的proceed方法

@Override public Response proceed(Request request) throws IOException {
  return proceed(request, streamAllocation, httpCodec, connection);
}

調(diào)用四個(gè)參數(shù)的proceed方法

@Override public Response proceed(Request request) throws IOException {
  return proceed(request, streamAllocation, httpCodec, connection);
}

public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
    RealConnection connection) 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.httpCodec != null && !this.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.httpCodec != 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, streamAllocation, httpCodec,
      connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
      writeTimeout);
  Interceptor interceptor = interceptors.get(index);
  Response response = interceptor.intercept(next);

  // Confirm that the next interceptor made its required call to chain.proceed().
  if (httpCodec != 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;
}

這里將index值加1,并構(gòu)建了下一個(gè)攔截器鏈對(duì)象next,然后獲取到當(dāng)前index=0的攔截器對(duì)象interceptor,調(diào)用其intercept方法執(zhí)行該攔截器邏輯,并將next對(duì)象傳入方法中。我們看一下第一個(gè)攔截器retryAndFollowUpInterceptor的intercept方法的一個(gè)關(guān)鍵邏輯

public Response intercept(Chain chain) throws IOException {
  Request request = chain.request();
  RealInterceptorChain realChain = (RealInterceptorChain) chain;
  //...
  response = realChain.proceed(request, streamAllocation, null, null);
  //...
  }

這里獲取前面構(gòu)建的index為1的next攔截器對(duì)象,調(diào)用了它的proceed方法,又會(huì)獲取到index為1的BridgeInterceptor攔截器并調(diào)用其intercept方法,所以會(huì)依次執(zhí)行下一個(gè)攔截器的intercept的方法完成網(wǎng)絡(luò)請(qǐng)求操作。

下面依次看一下OkHttp提供的5個(gè)攔截器的主要邏輯和作用

RetryAndFollowUpInterceptor重試和重定向攔截器

該攔截器負(fù)責(zé)當(dāng)請(qǐng)求發(fā)生錯(cuò)誤時(shí)進(jìn)行錯(cuò)誤重試,以及對(duì)請(qǐng)求進(jìn)行重定向。當(dāng)請(qǐng)求取消時(shí),它會(huì)拋出IOException異常。

@Override public Response intercept(Chain chain) throws IOException {
  Request request = chain.request();
  RealInterceptorChain realChain = (RealInterceptorChain) chain;
  Call call = realChain.call();
  EventListener eventListener = realChain.eventListener();

  StreamAllocation streamAllocation = new StreamAllocation(client.connectionPool(),
      createAddress(request.url()), call, eventListener, callStackTrace);
  this.streamAllocation = streamAllocation;

  int followUpCount = 0;
  Response priorResponse = null;
  while (true) {
    if (canceled) {
      streamAllocation.release();
      throw new IOException("Canceled");
    }

    Response response;
    boolean releaseConnection = true;
    try {
      response = realChain.proceed(request, streamAllocation, null, null);
      releaseConnection = false;
    } catch (RouteException e) {
      // The attempt to connect via a route failed. The request will not have been sent.
      if (!recover(e.getLastConnectException(), streamAllocation, false, request)) {
        throw e.getFirstConnectException();
      }
      releaseConnection = false;
      continue;
    } catch (IOException e) {
      // An attempt to communicate with a server failed. The request may have been sent.
      boolean requestSendStarted = !(e instanceof ConnectionShutdownException);
      if (!recover(e, streamAllocation, requestSendStarted, request)) throw e;
      releaseConnection = false;
      continue;
    } finally {
      // We're throwing an unchecked exception. Release any resources.
      if (releaseConnection) {
        streamAllocation.streamFailed(null);
        streamAllocation.release();
      }
    }

    // Attach the prior response if it exists. Such responses never have a body.
    if (priorResponse != null) {
      response = response.newBuilder()
          .priorResponse(priorResponse.newBuilder()
                  .body(null)
                  .build())
          .build();
    }

    Request followUp;
    try {
      followUp = followUpRequest(response, streamAllocation.route());
    } catch (IOException e) {
      streamAllocation.release();
      throw e;
    }

    if (followUp == null) {
      streamAllocation.release();
      return response;
    }

    closeQuietly(response.body());

    if (++followUpCount > MAX_FOLLOW_UPS) {
      streamAllocation.release();
      throw new ProtocolException("Too many follow-up requests: " + followUpCount);
    }

    if (followUp.body() instanceof UnrepeatableRequestBody) {
      streamAllocation.release();
      throw new HttpRetryException("Cannot retry streamed HTTP body", response.code());
    }

    if (!sameConnection(response, followUp.url())) {
      streamAllocation.release();
      streamAllocation = new StreamAllocation(client.connectionPool(),
          createAddress(followUp.url()), call, eventListener, callStackTrace);
      this.streamAllocation = streamAllocation;
    } else if (streamAllocation.codec() != null) {
      throw new IllegalStateException("Closing the body of " + response
          + " didn't close its backing stream. Bad interceptor?");
    }
    request = followUp;
    priorResponse = response;
  }
}

方法中首先構(gòu)建一個(gè)StreamAllocation對(duì)象,然后進(jìn)入while(true)循環(huán),如果標(biāo)志位canceled為true會(huì)直接拋出IOException異常。

調(diào)用攔截器鏈realChain的proceed方法繼續(xù)執(zhí)行下一個(gè)攔截器的邏輯,如果發(fā)生了RouteException異常,進(jìn)入第一個(gè)catch塊

// The attempt to connect via a route failed. The request will not have been sent.
if (!recover(e.getLastConnectException(), streamAllocation, false, request)) {
  throw e.getFirstConnectException();
}
releaseConnection = false;
continue;

這里判斷如果這個(gè)請(qǐng)求是不可以恢復(fù)的話,會(huì)直接拋出異常,否則continue繼續(xù)while循環(huán)執(zhí)行請(qǐng)求。當(dāng)發(fā)生IOException異常時(shí)進(jìn)入第二個(gè)catch塊,判斷邏輯和前面相同,如果可恢復(fù)的話則繼續(xù)while循環(huán),在finally中進(jìn)行了資源的釋放。上面就完成了請(qǐng)求的重試。

調(diào)用followUpRequest方法

private Request followUpRequest(Response userResponse, Route route) throws IOException {
  if (userResponse == null) throw new IllegalStateException();
  int responseCode = userResponse.code();

  final String method = userResponse.request().method();
  switch (responseCode) {
    case HTTP_PROXY_AUTH:
      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:
      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")) {
        return null;
      }
      // fall-through
    //...省略部分代碼

    case HTTP_CLIENT_TIMEOUT:
      if (!client.retryOnConnectionFailure()) {
        return null;
      }

      if (userResponse.request().body() instanceof UnrepeatableRequestBody) {
        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();
        //...省略部分代碼
    default:
      return null;
  }
}

這個(gè)方法會(huì)配置請(qǐng)求以接收響應(yīng),它會(huì)添加請(qǐng)求的認(rèn)證頭部,對(duì)請(qǐng)求進(jìn)行重定向,或者處理請(qǐng)求超時(shí)情況。如果方法中判斷這幾種情況都不需要處理的話,會(huì)返回null,否則會(huì)返回一個(gè)request。判斷如果得到的followUp為空的話,會(huì)直接返回當(dāng)前響應(yīng)對(duì)象,跳出當(dāng)前循環(huán)。上面就完成了請(qǐng)求重定向的處理,該攔截器的操作就分析完了。

BridgeInterceptor 橋攔截器

該攔截器是應(yīng)用層代碼和網(wǎng)絡(luò)代碼的橋梁。首先它將用戶的請(qǐng)求構(gòu)建成網(wǎng)絡(luò)請(qǐng)求。然后它繼續(xù)請(qǐng)求網(wǎng)絡(luò),最后它將網(wǎng)絡(luò)響應(yīng)構(gòu)建成用戶響應(yīng)返回。下面分析intercept方法中的這幾個(gè)操作

RequestBody body = userRequest.body();
if (body != null) {
  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()) {
  requestBuilder.header("Cookie", cookieHeader(cookies));
}

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

上面判斷用戶請(qǐng)求中如果沒(méi)有配置一些請(qǐng)求頭,則會(huì)補(bǔ)全配置Content-Type、Content-Length、Host和Connection這些請(qǐng)求頭。如果我們配置了可以接收gzip的壓縮數(shù)據(jù),我們也要負(fù)責(zé)解壓傳輸流。判斷獲取到的cookies不為空的話,也將Cookie添加到header中。

在配置完請(qǐng)求頭后,調(diào)用攔截器鏈的proceed方法發(fā)起請(qǐng)求獲得響應(yīng)

Response networkResponse = chain.proceed(requestBuilder.build());

得到響應(yīng),接收headers保存cookies信息,如下

HttpHeaders.receiveHeaders(cookieJar, userRequest.url(), networkResponse.headers());

最后判斷如果設(shè)置了可以接收gzip壓縮數(shù)據(jù)流,則會(huì)解壓壓縮流數(shù)據(jù)。最后將處理完后的響應(yīng)返回

if (transparentGzip
    && "gzip".equalsIgnoreCase(networkResponse.header("Content-Encoding"))
    && HttpHeaders.hasBody(networkResponse)) {
  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();

CacheInterceptor 緩存攔截器

顧名思義,這個(gè)就是處理網(wǎng)絡(luò)請(qǐng)求時(shí)的緩存的攔截器。下面分析intercept方法的邏輯

Response cacheCandidate = cache != null
    ? cache.get(chain.request())
    : null;

long now = System.currentTimeMillis();

CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();
Request networkRequest = strategy.networkRequest;
Response cacheResponse = strategy.cacheResponse;

首先獲取緩存的Response,將請(qǐng)求request和獲取到的緩存?zhèn)魅氲紺acheStrategy的Factory中,并調(diào)用get方法獲取到緩存策略對(duì)象。緩存策略的主要作用是根據(jù)傳入的request和response判斷是使用網(wǎng)絡(luò),緩存還是兩者都要。取得緩存策略的過(guò)程會(huì)向請(qǐng)求頭添加條件,例如If-Modified-Since;或者如果緩存響應(yīng)數(shù)據(jù)是可能過(guò)期的話,會(huì)為其添加warnings。

if (cacheCandidate != null && cacheResponse == null) {
  closeQuietly(cacheCandidate.body()); // The cache candidate wasn't applicable. Close it.
}

如果從緩存策略獲取到的響應(yīng)為空的話,則證明緩存不可用,關(guān)閉響應(yīng)。

// 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.
    if (networkRequest == null) {
      return cacheResponse.newBuilder()
          .cacheResponse(stripBody(cacheResponse))
          .build();
    }

如果我們配置了不使用網(wǎng)絡(luò),只使用緩存。而此時(shí)從緩存策略中獲取到的響應(yīng)為空的話,構(gòu)建一個(gè)失敗的504的響應(yīng)返回,否則直接返回緩存響應(yīng)。

Response networkResponse = null;
try {
  networkResponse = chain.proceed(networkRequest);
} finally {
  // If we're crashing on I/O or otherwise, don't leak the cache body.
  if (networkResponse == null && cacheCandidate != null) {
    closeQuietly(cacheCandidate.body());
  }
}

跟上面的攔截器一樣,調(diào)用攔截器鏈的proceed方法繼續(xù)執(zhí)行下一個(gè)攔截器邏輯并獲取響應(yīng)。

// If we have a cache response too, then we're doing a conditional get.
if (cacheResponse != null) {
  if (networkResponse.code() == HTTP_NOT_MODIFIED) {
    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());
  }
}

如果從緩存策略中獲取到的響應(yīng)不為空,再判斷網(wǎng)絡(luò)請(qǐng)求返回的code為304即服務(wù)器無(wú)修改的話,我們可以直接使用緩存響應(yīng)。

Response response = networkResponse.newBuilder()
    .cacheResponse(stripBody(cacheResponse))
    .networkResponse(stripBody(networkResponse))
    .build();

if (cache != null) {
  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;

如果上面的條件都不滿足的話,那么我們就是用網(wǎng)絡(luò)請(qǐng)求得到的響應(yīng),判斷響應(yīng)有body并且是可緩存的話就將這個(gè)響應(yīng)保存到緩存中。最后判斷如果這個(gè)請(qǐng)求的方法是不可以緩存的話,會(huì)將其從緩存中移除,這樣緩存攔截器的主流程就走完了。

ConnectInterceptor 連接攔截器

連接攔截器打開(kāi)一個(gè)到目標(biāo)服務(wù)器的連接,并且調(diào)用下一個(gè)攔截器。

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

  // We need the network to satisfy this request. Possibly for validating a conditional GET.
  boolean doExtensiveHealthChecks = !request.method().equals("GET");
  HttpCodec httpCodec = streamAllocation.newStream(client, chain, doExtensiveHealthChecks);
  RealConnection connection = streamAllocation.connection();

  return realChain.proceed(request, streamAllocation, httpCodec, connection);
}

獲取到重試和重定向攔截器中初始化streamAllocation對(duì)象,調(diào)用newStream方法

public HttpCodec newStream(
    OkHttpClient client, Interceptor.Chain chain, boolean doExtensiveHealthChecks) {
    //...省略部分代碼
  try {
    RealConnection resultConnection = findHealthyConnection(connectTimeout, readTimeout,
        writeTimeout, pingIntervalMillis, connectionRetryEnabled, doExtensiveHealthChecks);
    HttpCodec resultCodec = resultConnection.newCodec(client, chain, this);

    synchronized (connectionPool) {
      codec = resultCodec;
      return resultCodec;
    }
  } catch (IOException e) {
    throw new RouteException(e);
  }
}

調(diào)用findHealthyConnection

private RealConnection findHealthyConnection(int connectTimeout, int readTimeout,
    int writeTimeout, int pingIntervalMillis, boolean connectionRetryEnabled,
    boolean doExtensiveHealthChecks) throws IOException {
  while (true) {
    RealConnection candidate = findConnection(connectTimeout, readTimeout, writeTimeout,
        pingIntervalMillis, connectionRetryEnabled);

    // If this is a brand new connection, we can skip the extensive health checks.
    synchronized (connectionPool) {
      if (candidate.successCount == 0) {
        return candidate;
      }
    }
    // Do a (potentially slow) check to confirm that the pooled connection is still good. If it
    // isn't, take it out of the pool and start again.
    if (!candidate.isHealthy(doExtensiveHealthChecks)) {
      noNewStreams();
      continue;
    }
    return candidate;
  }
}

這個(gè)方法會(huì)一直執(zhí)行while循環(huán)直接到找個(gè)一個(gè)健康的連接。調(diào)用findConnection方法,該方法中的方法里邏輯較多,分步來(lái)看

releasedConnection = this.connection;
toClose = releaseIfNoNewStreams();
if (this.connection != null) {
  // We had an already-allocated connection and it's good.
  result = this.connection;
  releasedConnection = null;
}
if (!reportedAcquired) {
  // If the connection was never reported acquired, don't report it as released!
  releasedConnection = null;
}

首先嘗試使用已經(jīng)分配過(guò)的connection連接,如果connection不為空,即我們可以使用它。

if (result == null) {
    // Attempt to get a connection from the pool.
    Internal.instance.get(connectionPool, address, this, null);
    if (connection != null) {
      foundPooledConnection = true;
      result = connection;
    } else {
      selectedRoute = route;
    }
  }
}
closeQuietly(toClose);

if (releasedConnection != null) {
  eventListener.connectionReleased(call, releasedConnection);
}
if (foundPooledConnection) {
  eventListener.connectionAcquired(call, result);
}
if (result != null) {
  // If we found an already-allocated or pooled connection, we're done.
  return result;
}

如果connection為空,會(huì)試圖從連接池中獲取,如果connection不為空將其賦值給result。到這里如果我們找到了已經(jīng)分配的connection或者是連接池中的connection,就將connection返回。

//...
result = new RealConnection(connectionPool, selectedRoute);
//...

如果上面都沒(méi)找到可用的connection,這里實(shí)例化了一個(gè)新的connection賦值給result。

// Do TCP + TLS handshakes. This is a blocking operation.
result.connect(connectTimeout, readTimeout, writeTimeout, pingIntervalMillis,
    connectionRetryEnabled, call, eventListener);
routeDatabase().connected(result.route());

調(diào)用了result的connect方法建立了連接,其內(nèi)部是建立的socket連接,內(nèi)部具體細(xì)節(jié)暫時(shí)不深究。最后調(diào)用connection的newCodec方法獲取了HttpCodec對(duì)象,HttpCodec對(duì)象封裝了okio的輸入和輸出流對(duì)象。

將request和streamAllocation以及這步獲得的connection、httpCodec對(duì)象傳入到攔截器鏈的proceed方法調(diào)用下一個(gè)攔截器。

CallServerInterceptor 請(qǐng)求服務(wù)攔截器

這是攔截器鏈中的最后一個(gè)攔截器,它負(fù)責(zé)向服務(wù)器發(fā)送網(wǎng)絡(luò)請(qǐng)求。

看一下它interceptor方法具體發(fā)送網(wǎng)絡(luò)請(qǐng)求的流程:

httpCodec.writeRequestHeaders(request);

這里準(zhǔn)備請(qǐng)求頭,并將其寫(xiě)入到流中發(fā)送到服務(wù)器。

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"))) {
    httpCodec.flushRequest();
    realChain.eventListener().responseHeadersStart(realChain.call());
    responseBuilder = httpCodec.readResponseHeaders(true);
  }

  if (responseBuilder == null) {
    // Write the request body if the "Expect: 100-continue" expectation was met.
    realChain.eventListener().requestBodyStart(realChain.call());
    long contentLength = request.body().contentLength();
    CountingSink requestBodyOut =
        new CountingSink(httpCodec.createRequestBody(request, contentLength));
    BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);

    request.body().writeTo(bufferedRequestBody);
    bufferedRequestBody.close();
    realChain.eventListener()
        .requestBodyEnd(realChain.call(), requestBodyOut.successfulCount);
  } else if (!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.
    streamAllocation.noNewStreams();
  }
}

httpCodec.finishRequest();

這里判斷如果請(qǐng)求頭中有Expect: 100-continue信息,則請(qǐng)求時(shí)會(huì)先不發(fā)送請(qǐng)求體信息,直到接收到服務(wù)器的100-continue應(yīng)答后才會(huì)將數(shù)據(jù)post給服務(wù)器。

Expect: 100-continue解釋?zhuān)嚎蛻舳讼劝l(fā)送了一個(gè)請(qǐng)求,這個(gè)請(qǐng)求的header中包含了一個(gè)屬性expect: 100-continue。這種情況一般出現(xiàn)于上傳大容量body或者是需要驗(yàn)證的時(shí)候。這時(shí)服務(wù)器會(huì)讀取請(qǐng)求的header并返回一個(gè)100 continue的響應(yīng),如果服務(wù)器可以提供這項(xiàng)服務(wù)的話??蛻舳嗽賹ttp請(qǐng)求發(fā)送回去。然后服務(wù)器會(huì)讀取請(qǐng)求的body并且在成功后返回200狀態(tài)碼。這樣做可以避免帶寬浪費(fèi)。

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
  responseBuilder = httpCodec.readResponseHeaders(false);

  response = responseBuilder
          .request(request)
          .handshake(streamAllocation.connection().handshake())
          .sentRequestAtMillis(sentRequestMillis)
          .receivedResponseAtMillis(System.currentTimeMillis())
          .build();

  code = response.code();
}

如果我們沒(méi)有指定Expect: 100-continue請(qǐng)求,服務(wù)器也返回了100-continue響應(yīng)的話,會(huì)再次嘗試獲取的真實(shí)的響應(yīng)。

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

如果收到了101響應(yīng),即服務(wù)端理解了客戶端的請(qǐng)求,并通知客戶端采取不同的協(xié)議,我們會(huì)重新構(gòu)建response以確保攔截器收到是非空的響應(yīng),否則直接拿到響應(yīng)。

if ("close".equalsIgnoreCase(response.request().header("Connection"))
    || "close".equalsIgnoreCase(response.header("Connection"))) {
  streamAllocation.noNewStreams();
}

當(dāng)請(qǐng)求的頭部字段或者響應(yīng)的頭部字段Connection為close時(shí),則會(huì)斷開(kāi)連接,并且會(huì)禁止connection產(chǎn)生新的流。

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

這里狀態(tài)碼表示服務(wù)器接收的請(qǐng)求已成功處理,但在請(qǐng)求報(bào)文中不含實(shí)體的主體部分。另外也不允許返回任何實(shí)體的主體。

這里判斷如果響應(yīng)碼是204或者205,但是響應(yīng)報(bào)文包含實(shí)體部分時(shí)就會(huì)拋出異常,否則將得到的response返回。

最后隨著OkHttp攔截器鏈中攔截器的依次執(zhí)行,OkHttp網(wǎng)絡(luò)請(qǐng)求就結(jié)束了。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容