OKHttp源碼分析

大致的使用是這樣的:

OkHttpClient client = ...
OkHttpClient clientWith30sTimeout = client.newBuilder()       
.readTimeout(30, TimeUnit.SECONDS)
.build();
Response response = clientWith30sTimeout.newCall(request).execute();

OkHttpClient是產(chǎn)生Call的工廠類,而Call是為執(zhí)行網(wǎng)絡(luò)請(qǐng)求做準(zhǔn)備的一個(gè)接口,Call可以被取消,call對(duì)象代表一個(gè)單獨(dú)的request/response對(duì)。

public interface Call {
    Request request();
    Response execute() throws IOException;
    void enqueue(Callback responseCallback);
    void cancel();
    boolean isExecuted();
    boolean isCanceled();
    interface Factory {
        Call newCall(Request request);
    }
}

一,究竟一個(gè)請(qǐng)求/響應(yīng)通路是怎么的呢?
OkHttpClient類有個(gè)SocketFactory的成員,這是產(chǎn)生Socket的抽象工廠類,它有5個(gè)重載的抽象方法。

public abstract SocketcreateSocket(InetAddress address, int port,    InetAddress localAddress, int localPort)
throws IOException;

其4個(gè)參數(shù)含義分別是:服務(wù)端地址,服務(wù)端端口,本機(jī)地址,本機(jī)端口。這個(gè)抽象類有個(gè)默認(rèn)的實(shí)現(xiàn)類DefaultSocketFactory,它創(chuàng)建Socket方法的語(yǔ)句:

return new Socket(address, port, clientAddress, clientPort);

OkHttpClient在構(gòu)造時(shí)做了哪些事情呢?

private 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.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 = systemDefaultTrustManager();
    this.sslSocketFactory = systemDefaultSslSocketFactory(trustManager);
    this.certificateChainCleaner = CertificateChainCleaner.get(trustManager);
  }
  this.hostnameVerifier = builder.hostnameVerifier;
  this.certificatePinner = builder.certificatePinner.withCertificateChainCleaner(      certificateChainCleaner);
  this.proxyAuthenticator = builder.proxyAuthenticator;
  this.authenticator = builder.authenticator;
  this.connectionPool = builder.connectionPool;
  this.dns = builder.dns;
  this.followSslRedirects = builder.followSslRedirects;
  this.followRedirects = builder.followRedirects;
  this.retryOnConnectionFailure = builder.retryOnConnectionFailure;
  this.connectTimeout = builder.connectTimeout;
  this.readTimeout = builder.readTimeout;
  this.writeTimeout = builder.writeTimeout;
}

包括代理,協(xié)議,攔截器,緩存,socket,連接池,dns,超時(shí),重試等一些請(qǐng)求的準(zhǔn)備工作。這里面每一項(xiàng)具體如何發(fā)揮作用的,等后面分析。

當(dāng)一個(gè)OkHttpClient構(gòu)造好之后,調(diào)用newCall(Request request)方法產(chǎn)生一個(gè)Call對(duì)象,下面看一個(gè)實(shí)現(xiàn)了Call接口的類RealCall。

@Override
 public Response execute() throws IOException {
  synchronized (this) {
    if (executed) throw new IllegalStateException("Already Executed");
    executed = true;
  }
  try {
    client.dispatcher().executed(this);
    Response result = getResponseWithInterceptorChain(false);
    if (result == null) throw new IOException("Canceled");
    return result;
  } finally {
    client.dispatcher().finished(this);
  }
}

重點(diǎn)看Response是怎么得到的。

private Response getResponseWithInterceptorChain(boolean forWebSocket) throws IOException {
  Interceptor.Chain chain = new ApplicationInterceptorChain(0, originalRequest, forWebSocket);
  return chain.proceed(originalRequest);
}

Response是由ApplicationInterceptorChain來得到的。

@Override public Response proceed(Request request) throws IOException {
  // If there's another interceptor in the chain, call that.
  if (index < client.interceptors().size()) {
    Interceptor.Chain chain = new ApplicationInterceptorChain(index + 1, request, forWebSocket);
    Interceptor interceptor = client.interceptors().get(index);
    Response interceptedResponse = interceptor.intercept(chain);
    if (interceptedResponse == null) {
      throw new NullPointerException("application interceptor " + interceptor          + " returned null");
    } 
   return interceptedResponse;
  }
  // No more interceptors. Do HTTP.  return getResponse(request, forWebSocket);
}

到底所謂攔截器Interceptor和鏈Chain是干嘛的呢?注釋說了:

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.

大概意思就是觀察/修改/短路請(qǐng)求發(fā)出和短路回來的響應(yīng)。通常攔截器增加/移除或轉(zhuǎn)化request/response的頭部。

public interface Interceptor {
  Response intercept(Chain chain) throws IOException;
  interface Chain {
    Request request();
    Response proceed(Request request) throws IOException; 
   Connection connection();
  }
}

下面是ApplicationInterceptorChain對(duì)Interceptor.Chain的proceed(Request r)方法的具體實(shí)現(xiàn):

@Override public Response proceed(Request request) throws IOException {
  // If there's another interceptor in the chain, call that.
  if (index < client.interceptors().size()) {
    Interceptor.Chain chain = new ApplicationInterceptorChain(index + 1, request, forWebSocket);
    Interceptor interceptor = client.interceptors().get(index);
    Response interceptedResponse = interceptor.intercept(chain);
    if (interceptedResponse == null) {
      throw new NullPointerException("application interceptor " + interceptor          + " returned null");
    }    return interceptedResponse;
  }
  // No more interceptors. Do HTTP.
  return getResponse(request, forWebSocket);
}

攔截器起什么作用,這里要搞清楚,似乎很重要。
關(guān)鍵點(diǎn)在最后一句,這里返回了Response。

Response getResponse(Request request, boolean forWebSocket) throws IOException {
  // Copy body metadata to the appropriate request headers.
  RequestBody body = request.body();
  if (body != null) {
    Request.Builder requestBuilder = request.newBuilder();
    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");
    }    
request = requestBuilder.build();
  }
  // Create the initial HTTP engine. Retries and redirects need new engine for each attempt.
  engine = new HttpEngine(client, request, false, false, forWebSocket, null, null, null);
  int followUpCount = 0;  while (true) { 
   if (canceled) {
      engine.releaseStreamAllocation();
      throw new IOException("Canceled");
    }
    boolean releaseConnection = true;
    try {
      engine.sendRequest();
      engine.readResponse();
      releaseConnection = false;
    } catch (RequestException e) {
      // The attempt to interpret the request failed. Give up.
      throw e.getCause();
    } catch (RouteException e) {
      // The attempt to connect via a route failed. The request will not have been sent.
      HttpEngine retryEngine = engine.recover(e.getLastConnectException(), true, null);
      if (retryEngine != null) {
        releaseConnection = false;
        engine = retryEngine;
        continue;
      }
      // Give up; recovery is not possible.
      throw e.getLastConnectException();
    } catch (IOException e) {
      // An attempt to communicate with a server failed. The request may have been sent.
      HttpEngine retryEngine = engine.recover(e, false, null);
      if (retryEngine != null) {
        releaseConnection = false;
        engine = retryEngine;
        continue;
      }
      // Give up; recovery is not possible.
      throw e;
    } finally {
      // We're throwing an unchecked exception. Release any resources.
      if (releaseConnection) {
        StreamAllocation streamAllocation = engine.close();
        streamAllocation.release();
      }
    } 
    Response response = engine.getResponse();
    Request followUp = engine.followUpRequest();
    if (followUp == null) {
      if (!forWebSocket) {
        engine.releaseStreamAllocation();
      }
      return response;
    }
    StreamAllocation streamAllocation = engine.close();
    if (++followUpCount > MAX_FOLLOW_UPS) {
      streamAllocation.release();
      throw new ProtocolException("Too many follow-up requests: " + followUpCount);
    }
    if (!engine.sameConnection(followUp.url())) {
      streamAllocation.release();
      streamAllocation = null;
    }
 else if (streamAllocation.stream() != null) {
      throw new IllegalStateException("Closing the body of " + response          + " didn't close its backing stream. Bad interceptor?");
    }
    request = followUp;
    engine = new HttpEngine(client, request, false, false, forWebSocket, streamAllocation, null,        response);
  }
}

發(fā)現(xiàn)HTTP請(qǐng)求/響應(yīng)發(fā)生的核心,是HttpEngine這個(gè)類。

engine.sendRequest();
engine.readResponse();

大概是這兩個(gè)方法調(diào)用之后,就可以從engine中取出Response了。

Response response = engine.getResponse();

那么在HttpEngine調(diào)sendRequest()方法時(shí),發(fā)生了什么事呢?
我把這個(gè)方法的注釋摘抄一下:

Figures out what the response source will be, and opens a socket to that source if necessary. Prepares the request headers and gets ready to start writing the request body if it exists.

大意:指明response源是什么樣子,打開一個(gè)socket連接到那個(gè)response源上,準(zhǔn)備請(qǐng)求頭并做好開始寫請(qǐng)求體的準(zhǔn)備。

然后看一下readResponse()這個(gè)方法做了什么事情。

Flushes the remaining request header and body, parses the HTTP response headers and starts reading the HTTP response body if it exists.

大意是將剩余的請(qǐng)求頭和請(qǐng)求頭清除出來,然后解析HTTP響應(yīng)頭,開始讀取HTTP響應(yīng)體。

未完,待續(xù)。

下午要出去面試了。不知道又會(huì)是個(gè)什么鬼樣。

最后編輯于
?著作權(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)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

  • OkHttp源碼分析-同步篇 很早就想拿okhttp開刀了,這次就記一次使用OKhttp的網(wǎng)絡(luò)請(qǐng)求。首先需要說明的...
    埃賽爾閱讀 1,055評(píng)論 1 2
  • 因?yàn)轫?xiàng)目集成,在使用Fresco的時(shí)候,有集成OkHttp,所以接下來是OkHttp3.0的 簡(jiǎn)單一個(gè)列子,其中里...
    TragedyGo閱讀 1,592評(píng)論 0 1
  • OkHttp作為時(shí)下最受歡迎的網(wǎng)絡(luò)請(qǐng)求框架之一,它有著自己的優(yōu)點(diǎn): 使用了眾多的設(shè)計(jì)模式(如:Builder模式、...
    永恒之眼V閱讀 344評(píng)論 1 1
  • 主目錄見:Android高級(jí)進(jìn)階知識(shí)(這是總目錄索引)?OkHttp的知識(shí)點(diǎn)實(shí)在是不少,優(yōu)秀的思想也有很多,這里只...
    ZJ_Rocky閱讀 2,400評(píng)論 2 6
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,506評(píng)論 19 139

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