okhttp3 源碼詳細(xì)解析

前言

OkHttp是一個非常優(yōu)秀的網(wǎng)絡(luò)請求框架罩抗。目前比較流行的Retrofit也是默認(rèn)使用OkHttp的。所以O(shè)kHttp的源碼是一個不容錯過的學(xué)習(xí)資源灿椅。


基本使用

從使用方法出發(fā)套蒂,首先是怎么使用,其次是我們使用的功能在內(nèi)部是如何實現(xiàn)的.源碼地址

OkHttpClient client = new OkHttpClient();

String run(String url) throws IOException {
  Request request = new Request.Builder()
      .url(url)
      .build();

  Response response = client.newCall(request).execute();
  return response.body().string();
}

Request茫蛹、Response操刀、Call 基本概念

上面的代碼中涉及到幾個常用的類:Request、Response和Call婴洼。下面分別介紹:

Request

每一個HTTP請求包含一個URL骨坑、一個方法(GET或POST或其他)、一些HTTP頭柬采。請求還可能包含一個特定內(nèi)容類型的數(shù)據(jù)類的主體部分欢唾。

Response

響應(yīng)是對請求的回復(fù),包含狀態(tài)碼粉捻、HTTP頭和主體部分礁遣。

Call

OkHttp使用Call抽象出一個滿足請求的模型,盡管中間可能會有多個請求或響應(yīng)肩刃。執(zhí)行Call有兩種方式亡脸,同步或異步

簡介

這里寫圖片描述

在早期的版本中,OkHttp支持Http1.0,1.1,SPDY協(xié)議树酪,但是Http2協(xié)議的問世,導(dǎo)致OkHttp也做出了改變大州,OkHttp鼓勵開發(fā)者使用HTTP2续语,不再對SPDY協(xié)議給予支持。另外厦画,新版本的OkHttp還有一個新的亮點就是支持WebScoket疮茄,這樣我們就可以非常方便的建立長連接了滥朱。

作為一個優(yōu)秀的網(wǎng)絡(luò)框架,OkHttp同樣支持網(wǎng)絡(luò)緩存力试,OkHttp的緩存基于DiskLruCache,DiskLruCache雖然沒有被收入到Android的源碼中徙邻,但也是谷歌推薦的一個優(yōu)秀的緩存框架。有時間可以自己學(xué)習(xí)源碼畸裳,這里不再敘述缰犁。

在安全方便,OkHttp目前支持了如上圖所示的TLS版本怖糊,以確保一個安全的Socket連接帅容。
重試及重定向就不再說了,都知道什么意思伍伤,左上角給出了各瀏覽器或Http版本支持的重試或重定向次數(shù)并徘。


源碼分析

OKHttpClient

首先,我們生成了一個OKHttpClient對象扰魂,注意OKHttpClient對象的構(gòu)建是用Builder(構(gòu)建者)模式來構(gòu)建的麦乞。

public OkHttpClient() {
    this(new Builder());
  }
public Builder() {
      dispatcher = new Dispatcher();
      protocols = DEFAULT_PROTOCOLS;
      connectionSpecs = DEFAULT_CONNECTION_SPECS;
      eventListenerFactory = EventListener.factory(EventListener.NONE);
      proxySelector = ProxySelector.getDefault();
      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;
      connectTimeout = 10_000;
      readTimeout = 10_000;
      writeTimeout = 10_000;
      pingInterval = 0;
    }

可以看到我們簡單的一句new OkHttpClient(),OkHttp就已經(jīng)為我們做了很多工作劝评,很多我們需要的參數(shù)在這里都獲得默認(rèn)值姐直。各字段含義如下:

  • dispatcher:直譯就是調(diào)度器的意思。主要作用是通過雙端隊列保存Calls(同步&異步Call)付翁,同時在線程池中執(zhí)行異步請求简肴。后面會詳細(xì)解析該類。
  • protocols:默認(rèn)支持的Http協(xié)議版本 -- Protocol.HTTP_2, Protocol.HTTP_1_1百侧;
  • connectionSpecs:OKHttp連接(Connection)配置 -- ConnectionSpec.MODERN_TLS, -ConnectionSpec.CLEARTEXT砰识,我們分別看一下:
/** TLS 連接 */ 
public static final ConnectionSpec MODERN_TLS = new Builder(true)
      .cipherSuites(APPROVED_CIPHER_SUITES)
      .tlsVersions(TlsVersion.TLS_1_3, TlsVersion.TLS_1_2, TlsVersion.TLS_1_1, TlsVersion.TLS_1_0)
      .supportsTlsExtensions(true)
      .build();
/** 未加密、未認(rèn)證的Http連接. */
 public static final ConnectionSpec CLEARTEXT = new Builder(false).build();

可以看出一個是針對TLS連接的配置佣渴,一個是針對普通的Http連接的配置辫狼;

  • eventListenerFactory :一個Call的狀態(tài)監(jiān)聽器,注意這個是okhttp新添加的功能辛润,目前還不是最終版膨处,在后面的版本中會發(fā)生改變的。
  • proxySelector :使用默認(rèn)的代理選擇器砂竖;
  • cookieJar:默認(rèn)是沒有Cookie的真椿;
  • socketFactory:使用默認(rèn)的Socket工廠產(chǎn)生Socket;
  • hostnameVerifier乎澄、 certificatePinner突硝、 proxyAuthenticator、 authenticator:安全相關(guān)的設(shè)置置济;
  • connectionPool :連接池解恰;后面會詳細(xì)介紹锋八;
  • dns:這個一看就知道,域名解析系統(tǒng) domain name -> ip address护盈;
  • pingInterval :這個就和WebSocket有關(guān)了挟纱。為了保持長連接,我們必須間隔一段時間發(fā)送一個ping指令進(jìn)行备危活紊服;

RealCall

在我們定義了請求對象request之后,我們需要生成一個Call對象脏款,該對象代表了一個準(zhǔn)備被執(zhí)行的請求围苫。Call是可以被取消的。Call對象代表了一個request/response 對(Stream).還有就是一個Call只能被執(zhí)行一次撤师。執(zhí)行同步請求剂府,代碼如下(RealCall的execute方法):


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

首先如果executed等于true,說明已經(jīng)被執(zhí)行剃盾,如果再次調(diào)用執(zhí)行就拋出異常腺占。這說明了一個Call只能被執(zhí)行。注意此處同步請求與異步請求生成的Call對象的區(qū)別痒谴,執(zhí)行
異步請求代碼如下(RealCall的enqueue方法):


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

可以看到同步請求生成的是RealCall對象衰伯,而異步請求生成的是AsyncCall對象。AsyncCall說到底其實就是Runnable的子類积蔚。
接著上面繼續(xù)分析意鲸,如果可以執(zhí)行,則對當(dāng)前請求添加監(jiān)聽器等操作尽爆,然后將請求Call對象放入調(diào)度器Dispatcher中怎顾。最后由攔截器鏈中的各個攔截器來對該請求進(jìn)行處理,返回最終的Response漱贱。

Dispatcher -- 調(diào)度器

Dispatcher是保存同步和異步Call的地方槐雾,并負(fù)責(zé)執(zhí)行異步AsyncCall。

這里寫圖片描述
public final class Dispatcher {
  /** 最大并發(fā)請求數(shù)為64 */
  private int maxRequests = 64;
  /** 每個主機(jī)最大請求數(shù)為5 */
  private int maxRequestsPerHost = 5;

  /** 線程池 */
  private ExecutorService executorService;

  /** 準(zhǔn)備執(zhí)行的請求 */
  private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();

  /** 正在執(zhí)行的異步請求幅狮,包含已經(jīng)取消但未執(zhí)行完的請求 */
  private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();

  /** 正在執(zhí)行的同步請求募强,包含已經(jīng)取消單未執(zhí)行完的請求 */
  private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();

針對同步請求,Dispatcher使用了一個Deque保存了同步任務(wù)崇摄;針對異步請求擎值,Dispatcher使用了兩個Deque,一個保存準(zhǔn)備執(zhí)行的請求逐抑,一個保存正在執(zhí)行的請求幅恋,為什么要用兩個呢?因為Dispatcher默認(rèn)支持最大的并發(fā)請求是64個泵肄,單個Host最多執(zhí)行5個并發(fā)請求捆交,如果超過,則Call會先被放入到readyAsyncCall中腐巢,當(dāng)出現(xiàn)空閑的線程時品追,再將readyAsyncCall中的線程移入到runningAsynCalls中,執(zhí)行請求冯丙。先看Dispatcher的流程肉瓦,跟著流程讀源碼:

在OkHttp,使用如下構(gòu)造了單例線程池

public synchronized ExecutorService executorService() {
    if (executorService == null) {
      executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
          new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));
    }
    return executorService;
  }

構(gòu)造一個線程池ExecutorService:

executorService = new ThreadPoolExecutor(
//corePoolSize 最小并發(fā)線程數(shù),如果是0的話胃惜,空閑一段時間后所有線程將全部被銷毀
    0, 
//maximumPoolSize: 最大線程數(shù)泞莉,當(dāng)任務(wù)進(jìn)來時可以擴(kuò)充的線程最大值,當(dāng)大于了這個值就會根據(jù)丟棄處理機(jī)制來處理
    Integer.MAX_VALUE, 
//keepAliveTime: 當(dāng)線程數(shù)大于corePoolSize時船殉,多余的空閑線程的最大存活時間
    60, 
//單位秒
    TimeUnit.SECONDS,
//工作隊列,先進(jìn)先出
    new SynchronousQueue<Runnable>(),   
//單個線程的工廠         
   Util.threadFactory("OkHttp Dispatcher", false));

可以看出鲫趁,在Okhttp中,構(gòu)建了一個核心為[0, Integer.MAX_VALUE]的線程池蹲盘,它不保留任何最小線程數(shù)笔喉,隨時創(chuàng)建更多的線程數(shù)愿待,當(dāng)線程空閑時只能活60秒,它使用了一個不存儲元素的阻塞工作隊列疫剃,一個叫做”O(jiān)kHttp Dispatcher”的線程工廠。

也就是說硼讽,在實際運行中巢价,當(dāng)收到10個并發(fā)請求時,線程池會創(chuàng)建十個線程固阁,當(dāng)工作完成后壤躲,線程池會在60s后相繼關(guān)閉所有線程。

synchronized void enqueue(AsyncCall call) {
    if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
      runningAsyncCalls.add(call);
      executorService().execute(call);
    } else {
      readyAsyncCalls.add(call);
    }
  }

可以看到如果正在執(zhí)行的請求總數(shù)<=64 && 單個Host正在執(zhí)行的請求<=5您炉,則將請求加入到runningAsyncCalls集合中柒爵,緊接著就是利用線程池執(zhí)行該請求,否則就將該請求放入readyAsyncCalls集合中赚爵。上面我們已經(jīng)說了棉胀,AsyncCall是Runnable的子類(間接),因此冀膝,在線程池中最終會調(diào)用AsyncCall的execute()方法執(zhí)行異步請求:

 @Override protected void execute() {
      boolean signalledCallback = false;
      try {
        Response response = getResponseWithInterceptorChain();//攔截器鏈
        if (retryAndFollowUpInterceptor.isCanceled()) {//重試失敗唁奢,回調(diào)onFailure方法
          signalledCallback = true;
          responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
        } else {
          signalledCallback = true;
          responseCallback.onResponse(RealCall.this, response);
        }
      } catch (IOException 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);//結(jié)束
      }
    }

此處的執(zhí)行邏輯和同步的執(zhí)行邏輯基本相同,區(qū)別在最后一句代碼:client.dispatcher().finished(this);因為這是一個異步任務(wù)窝剖,所以會調(diào)用另外一個finish方法:

void finished(AsyncCall call) {
    finished(runningAsyncCalls, call, true);
  }
private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {
    int runningCallsCount;
    Runnable idleCallback;
    synchronized (this) {
      if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");//將請求移除集合
      if (promoteCalls) promoteCalls();
     ...
    }
 
   ...
  }

可以看到最后一個參數(shù)是true麻掸,這意味著需要執(zhí)行promoteCalls方法:


private void promoteCalls() {
    if (runningAsyncCalls.size() >= maxRequests) return; // Already running max capacity.
    if (readyAsyncCalls.isEmpty()) return; // No ready calls to promote.
 
    for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
      AsyncCall call = i.next();
 
      if (runningCallsForHost(call) < maxRequestsPerHost) {
        i.remove();
        runningAsyncCalls.add(call);
        executorService().execute(call);
      }
      if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity.
    }
  }

該方法主要是遍歷執(zhí)行readyRunningCalls集合中待執(zhí)行的請求,當(dāng)然前提是正在執(zhí)行的Call總數(shù)沒有超過64赐纱,并且readyAsyncCalls集合不為空脊奋。如果readyAsyncCalls集合為空熬北,則意味著請求差不多都執(zhí)行了。放入runningAsyncCalls集合中的請求會繼續(xù)走上述的流程诚隙,直到全部的請求被執(zhí)行讶隐。

InterceptorChain(攔截器鏈)

在介紹RealCall 中源碼的時候

client.dispatcher().executed(this);
Response result = getResponseWithInterceptorChain();

上面已經(jīng)介紹了分發(fā)器Dispatcher
下面就介紹核心重點 getResponseWithInterceptorChain:

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);
    return chain.proceed(originalRequest);
  }
這里寫圖片描述

可以看到,在該方法中久又,我們依次添加了用戶自定義的interceptor巫延、retryAndFollowUpInterceptor、BridgeInterceptor地消、CacheInterceptor炉峰、ConnectInterceptor、 networkInterceptors脉执、CallServerInterceptor疼阔,并將這些攔截器傳遞給了這個RealInterceptorChain。

1)在配置 OkHttpClient 時設(shè)置的 interceptors适瓦;
2)負(fù)責(zé)失敗重試以及重定向的 RetryAndFollowUpInterceptor竿开;
3)負(fù)責(zé)把用戶構(gòu)造的請求轉(zhuǎn)換為發(fā)送到服務(wù)器的請求、把服務(wù)器返回的響應(yīng)轉(zhuǎn)換為用戶友好的響應(yīng)的 BridgeInterceptor玻熙;
4)負(fù)責(zé)讀取緩存直接返回否彩、更新緩存的 CacheInterceptor;
5)負(fù)責(zé)和服務(wù)器建立連接的 ConnectInterceptor嗦随;
6)配置 OkHttpClient 時設(shè)置的 networkInterceptors列荔;
7)負(fù)責(zé)向服務(wù)器發(fā)送請求數(shù)據(jù)、從服務(wù)器讀取響應(yīng)數(shù)據(jù)的 CallServerInterceptor枚尼。

OkHttp的這種攔截器鏈采用的是責(zé)任鏈模式贴浙,這樣的好處是將請求的發(fā)送和處理分開,并且可以動態(tài)添加中間的處理方實現(xiàn)對請求的處理署恍、短路等操作崎溃。

從上述源碼得知,不管okhttp有多少攔截器最后都會走盯质,如下方法:

Interceptor.Chain chain = new RealInterceptorChain(
        interceptors, null, null, null, 0, originalRequest);
return chain.proceed(originalRequest);

從方法名字基本可以猜到是干嘛的袁串,調(diào)用 chain.proceed(originalRequest); 將request傳遞進(jìn)來,從攔截器鏈里拿到返回結(jié)果呼巷。那么攔截器Interceptor是干嘛的囱修,Chain是干嘛的呢?繼續(xù)往下看RealInterceptorChain

RealInterceptorChain類

下面是RealInterceptorChain的定義王悍,該類實現(xiàn)了Chain接口破镰,在getResponseWithInterceptorChain調(diào)用時好幾個參數(shù)都傳的null。

public final class RealInterceptorChain implements Interceptor.Chain {

   public RealInterceptorChain(List<Interceptor> interceptors, StreamAllocation streamAllocation,
        HttpCodec httpCodec, RealConnection connection, int index, Request request) {
        this.interceptors = interceptors;
        this.connection = connection;
        this.streamAllocation = streamAllocation;
        this.httpCodec = httpCodec;
        this.index = index;
        this.request = request;
  }
  ......

 @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++;

    ......

    // Call the next interceptor in the chain.
    RealInterceptorChain next = new RealInterceptorChain(
        interceptors, streamAllocation, httpCodec, connection, index + 1, request);
    Interceptor interceptor = interceptors.get(index);
    Response response = interceptor.intercept(next);

   ......

    return response;
  }

  protected abstract void execute();
}

主要看proceed方法,proceed方法中判斷index(此時為0)是否大于或者等于client.interceptors(List )的大小鲜漩。由于httpStream為null源譬,所以首先創(chuàng)建next攔截器鏈,主需要把索引置為index+1即可孕似;然后獲取第一個攔截器瓶佳,調(diào)用其intercept方法。

Interceptor 代碼如下:

public interface Interceptor {
  Response intercept(Chain chain) throws IOException;

  interface Chain {
    Request request();

    Response proceed(Request request) throws IOException;

    Connection connection();
  }
}

RetryAndFollowUpInterceptor

負(fù)責(zé)失敗重試以及重定向

@Override 
public Response intercept(Chain chain) throws IOException {
        Request request = chain.request();
        streamAllocation = new StreamAllocation(
                client.connectionPool(), createAddress(request.url()));
        int followUpCount = 0;
        Response priorResponse = null;
        while (true) {
            if (canceled) {
                streamAllocation.release();
                throw new IOException("Canceled");
            }

            Response response = null;
            boolean releaseConnection = true;
            try {
                response = ((RealInterceptorChain) chain).proceed(request, streamAllocation, null, null);    //(1)
                releaseConnection = false;
            } catch (RouteException e) {
                // The attempt to connect via a route failed. The request will not have been sent.
                //通過路線連接失敗鳞青,請求將不會再發(fā)送
                if (!recover(e.getLastConnectException(), true, request)) throw e.getLastConnectException();
                releaseConnection = false;
                continue;
            } catch (IOException e) {
                // An attempt to communicate with a server failed. The request may have been sent.
                // 與服務(wù)器嘗試通信失敗,請求不會再發(fā)送为朋。
                if (!recover(e, false, 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.
            // 附加上先前存在的response。這樣的response從來沒有body
            // TODO: 2016/8/23 這里沒賦值习寸,豈不是一直為空胶惰?
            if (priorResponse != null) { //  (2)
                response = response.newBuilder()
                        .priorResponse(priorResponse.newBuilder()
                                .body(null)
                                .build())
                        .build();
            }

            Request followUp = followUpRequest(response); //判斷狀態(tài)碼 (3)
            if (followUp == null){
                if (!forWebSocket) {
                    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) {
                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()));
            } 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;
        }
    }

這里是最關(guān)鍵的代碼,可以看出在response = ((RealInterceptorChain) chain).proceed(request, streamAllocation, null, null);中直接調(diào)用了下一個攔截器霞溪,然后捕獲可能的異常來進(jìn)行操作

這里對于返回的response的狀態(tài)碼進(jìn)行判斷孵滞,然后進(jìn)行處理

BridgeInterceptor

BridgeInterceptor從用戶的請求構(gòu)建網(wǎng)絡(luò)請求,然后提交給網(wǎng)絡(luò)鸯匹,最后從網(wǎng)絡(luò)響應(yīng)中提取出用戶響應(yīng)坊饶。從最上面的圖可以看出,BridgeInterceptor實現(xiàn)了適配的功能殴蓬。下面是其intercept方法:

public final class BridgeInterceptor implements Interceptor {
  ......

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

 RequestBody body = userRequest.body();
 //如果存在請求主體部分匿级,那么需要添加Content-Type、Content-Length首部
 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());
  }

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

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

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

    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);
      responseBuilder.body(new RealResponseBody(strippedHeaders, Okio.buffer(responseBody)));
    }

    return responseBuilder.build();
  }

  /** Returns a 'Cookie' HTTP request header with all cookies, like {@code a=b; c=d}. */
  private String cookieHeader(List<Cookie> cookies) {
    StringBuilder cookieHeader = new StringBuilder();
    for (int i = 0, size = cookies.size(); i < size; i++) {
      if (i > 0) {
        cookieHeader.append("; ");
      }
      Cookie cookie = cookies.get(i);
      cookieHeader.append(cookie.name()).append('=').append(cookie.value());
    }
    return cookieHeader.toString();
  }
}

上面的代碼可以看出染厅,首先獲取原請求痘绎,然后在請求中添加頭,比如Host肖粮、Connection孤页、Accept-Encoding參數(shù)等,然后根據(jù)看是否需要填充Cookie涩馆,在對原始請求做出處理后行施,使用chain的procced方法得到響應(yīng),接下來對響應(yīng)做處理得到用戶響應(yīng)凌净,最后返回響應(yīng)悲龟。

CacheInterceptor

@Override
public Response intercept(Chain chain) throws IOException {
Response cacheCandidate = cache != null
? cache.get(chain.request()) //通過request得到緩存
: null;

long now = System.currentTimeMillis();

CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get(); //根據(jù)request來得到緩存策略
Request networkRequest = strategy.networkRequest;
Response cacheResponse = strategy.cacheResponse;

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

if (cacheCandidate != null && cacheResponse == null) { //存在緩存的response,但是不允許緩存
  closeQuietly(cacheCandidate.body()); // The cache candidate wasn't applicable. Close it. 緩存不適合冰寻,關(guān)閉
}

// If we're forbidden from using the network and the cache is insufficient, fail.
  //如果我們禁止使用網(wǎng)絡(luò)须教,且緩存為null,失敗
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(EMPTY_BODY)
      .sentRequestAtMillis(-1L)
      .receivedResponseAtMillis(System.currentTimeMillis())
      .build();
}

// If we don't need the network, we're done.
if (networkRequest == null) {  //沒有網(wǎng)絡(luò)請求,跳過網(wǎng)絡(luò)轻腺,返回緩存
  return cacheResponse.newBuilder()
      .cacheResponse(stripBody(cacheResponse))
      .build();
}

Response networkResponse = null;
try {
  networkResponse = chain.proceed(networkRequest);//網(wǎng)絡(luò)請求攔截器    //
} finally {
  // If we're crashing on I/O or otherwise, don't leak the cache body.
    //如果我們因為I/O或其他原因崩潰乐疆,不要泄漏緩存體
  if (networkResponse == null && cacheCandidate != null) {
    closeQuietly(cacheCandidate.body());
  }
}

// If we have a cache response too, then we're doing a conditional get.
  //如果我們有一個緩存的response,然后我們正在做一個條件GET
if (cacheResponse != null) {
  if (validate(cacheResponse, networkResponse)) { //比較確定緩存response可用
    Response response = cacheResponse.newBuilder()
        .headers(combine(cacheResponse.headers(), networkResponse.headers()))
        .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()).
      //更新緩存贬养,在剝離content-Encoding之前
    cache.trackConditionalCacheHit();
    cache.update(cacheResponse, response);
    return response;
  } else {
    closeQuietly(cacheResponse.body());
  }
}

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

if (HttpHeaders.hasBody(response)) {   
  CacheRequest cacheRequest = maybeCache(response, networkResponse.request(), cache);
  response = cacheWritingResponse(cacheRequest, response);
}

return response;

}

  • 首先挤土,根據(jù)request來判斷cache中是否有緩存的response,如果有误算,得到這個response仰美,然后進(jìn)行判斷當(dāng)前response是否有效,沒有將cacheCandate賦值為空儿礼。
  • 根據(jù)request判斷緩存的策略咖杂,是否要使用了網(wǎng)絡(luò),緩存 或兩者都使用
  • 調(diào)用下一個攔截器蚊夫,決定從網(wǎng)絡(luò)上來得到response
  • 如果本地已經(jīng)存在cacheResponse诉字,那么讓它和網(wǎng)絡(luò)得到的networkResponse做比較,決定是否來更新緩存的cacheResponse
  • 緩存未經(jīng)緩存過的response

ConnectInterceptor

public final class ConnectInterceptor implements Interceptor {
  ......

 @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, doExtensiveHealthChecks);
 RealConnection connection = streamAllocation.connection();

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

實際上建立連接就是創(chuàng)建了一個HttpCodec對象知纷,它將在后面的步驟中被使用壤圃,那它又是何方神圣呢?它是對 HTTP 協(xié)議操作的抽象琅轧,有兩個實現(xiàn):Http1Codec和Http2Codec伍绳,顧名思義,它們分別對應(yīng) HTTP/1.1 和 HTTP/2 版本的實現(xiàn)鹰晨。

在Http1Codec中墨叛,它利用 Okio 對Socket的讀寫操作進(jìn)行封裝,Okio 以后有機(jī)會再進(jìn)行分析模蜡,現(xiàn)在讓我們對它們保持一個簡單地認(rèn)識:它對java.io和java.nio進(jìn)行了封裝漠趁,讓我們更便捷高效的進(jìn)行 IO 操作。

而創(chuàng)建HttpCodec對象的過程涉及到StreamAllocation忍疾、RealConnection闯传,代碼較長,這里就不展開卤妒,這個過程概括來說甥绿,就是找到一個可用的RealConnection,再利用RealConnection的輸入輸出(BufferedSource和BufferedSink)創(chuàng)建HttpCodec對象则披,供后續(xù)步驟使用共缕。

NetworkInterceptors

配置OkHttpClient時設(shè)置的 NetworkInterceptors。

CallServerInterceptor

CallServerInterceptor是攔截器鏈中最后一個攔截器士复,負(fù)責(zé)將網(wǎng)絡(luò)請求提交給服務(wù)器图谷。它的intercept方法實現(xiàn)如下:

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

    long sentRequestMillis = System.currentTimeMillis();
    httpCodec.writeRequestHeaders(request);

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

      if (responseBuilder == null) {
        // Write the request body if the "Expect: 100-continue" expectation was met.
        Sink requestBodyOut = httpCodec.createRequestBody(request, request.body().contentLength());
        BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);
        request.body().writeTo(bufferedRequestBody);
        bufferedRequestBody.close();
      } 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();

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

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

    int code = response.code();
    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();
    }

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

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

    return response;
  }

從上面的代碼中可以看出翩活,首先獲取HttpStream對象,然后調(diào)用writeRequestHeaders方法寫入請求的頭部便贵,然后判斷是否需要寫入請求的body部分菠镇,最后調(diào)用finishRequest()方法將所有數(shù)據(jù)刷新給底層的Socket,接下來嘗試調(diào)用readResponseHeaders()方法讀取響應(yīng)的頭部承璃,然后再調(diào)用openResponseBody()方法得到響應(yīng)的body部分利耍,最后返回響應(yīng)。


總結(jié)

OkHttp的底層是通過Java的Socket發(fā)送HTTP請求與接受響應(yīng)的(這也好理解盔粹,HTTP就是基于TCP協(xié)議的)隘梨,但是OkHttp實現(xiàn)了連接池的概念,即對于同一主機(jī)的多個請求舷嗡,其實可以公用一個Socket連接出嘹,而不是每次發(fā)送完HTTP請求就關(guān)閉底層的Socket,這樣就實現(xiàn)了連接池的概念咬崔。而OkHttp對Socket的讀寫操作使用的OkIo庫進(jìn)行了一層封裝。

總體流程圖:


這里寫圖片描述

總體架構(gòu)圖:


這里寫圖片描述
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末烦秩,一起剝皮案震驚了整個濱河市垮斯,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌只祠,老刑警劉巖兜蠕,帶你破解...
    沈念sama閱讀 210,978評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異抛寝,居然都是意外死亡熊杨,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評論 2 384
  • 文/潘曉璐 我一進(jìn)店門盗舰,熙熙樓的掌柜王于貴愁眉苦臉地迎上來晶府,“玉大人,你說我怎么就攤上這事钻趋〈剑” “怎么了?”我有些...
    開封第一講書人閱讀 156,623評論 0 345
  • 文/不壞的土叔 我叫張陵蛮位,是天一觀的道長较沪。 經(jīng)常有香客問我,道長失仁,這世上最難降的妖魔是什么尸曼? 我笑而不...
    開封第一講書人閱讀 56,324評論 1 282
  • 正文 為了忘掉前任,我火速辦了婚禮萄焦,結(jié)果婚禮上控轿,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好解幽,可當(dāng)我...
    茶點故事閱讀 65,390評論 5 384
  • 文/花漫 我一把揭開白布贴见。 她就那樣靜靜地躺著,像睡著了一般躲株。 火紅的嫁衣襯著肌膚如雪片部。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,741評論 1 289
  • 那天霜定,我揣著相機(jī)與錄音档悠,去河邊找鬼。 笑死望浩,一個胖子當(dāng)著我的面吹牛辖所,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播磨德,決...
    沈念sama閱讀 38,892評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼缘回,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了典挑?” 一聲冷哼從身側(cè)響起酥宴,我...
    開封第一講書人閱讀 37,655評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎您觉,沒想到半個月后拙寡,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,104評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡琳水,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年肆糕,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片在孝。...
    茶點故事閱讀 38,569評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡诚啃,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出私沮,到底是詐尸還是另有隱情绍申,我是刑警寧澤,帶...
    沈念sama閱讀 34,254評論 4 328
  • 正文 年R本政府宣布顾彰,位于F島的核電站极阅,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏涨享。R本人自食惡果不足惜筋搏,卻給世界環(huán)境...
    茶點故事閱讀 39,834評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望厕隧。 院中可真熱鬧奔脐,春花似錦俄周、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,725評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至排龄,卻和暖如春波势,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背橄维。 一陣腳步聲響...
    開封第一講書人閱讀 31,950評論 1 264
  • 我被黑心中介騙來泰國打工尺铣, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人争舞。 一個月前我還...
    沈念sama閱讀 46,260評論 2 360
  • 正文 我出身青樓凛忿,卻偏偏與公主長得像,于是被迫代替她去往敵國和親竞川。 傳聞我的和親對象是個殘疾皇子店溢,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,446評論 2 348

推薦閱讀更多精彩內(nèi)容

  • 參考資源 官網(wǎng) 國內(nèi)博客 GitHub官網(wǎng) 鑒于一些關(guān)于OKHttp3源碼的解析文檔過于碎片化,本文系統(tǒng)的委乌,由淺入...
    風(fēng)骨依存閱讀 12,480評論 11 82
  • 這段時間老李的新公司要更換網(wǎng)絡(luò)層逞怨,知道現(xiàn)在主流網(wǎng)絡(luò)層的模式是RxJava+Retrofit+OKHttp,所以老李...
    隔壁老李頭閱讀 32,727評論 51 406
  • 簡介 OkHttp 是一款用于 Android 和 Java 的網(wǎng)絡(luò)請求庫,也是目前 Android 中最火的一個...
    然則閱讀 1,175評論 1 39
  • 關(guān)于okhttp是一款優(yōu)秀的網(wǎng)絡(luò)請求框架福澡,關(guān)于它的源碼分析文章有很多,這里分享我在學(xué)習(xí)過程中讀到的感覺比較好的文章...
    蕉下孤客閱讀 3,598評論 2 38
  • 1.安裝 由于官網(wǎng)的網(wǎng)速實在是太慢驹马,所以采用了清華大學(xué)開源軟件鏡像站的package革砸。 網(wǎng)站上有anconda和m...
    天津橋上閱讀 6,355評論 0 2