從OkHttp的源碼來看 HTTP

 先來了解一下OkHttp的歷史,最先是square公司以爲Android給的HttpClient這塊的庫不太好用,因而乎作了一層包裝,再後來他們包裝的這個庫被Android官方給收回去了,而Android內部的HttpUrlConnection的實現用的實際上是OkHttp的代碼,而Okhttp是徹底從新寫的一套HTTP庫,包含TCP、TLS的實現,最初是依賴於Google的那套網絡框架,而如今徹底不依賴了,最終還被Google給收購了,因此,好好了解OkHttp的本質是毋庸置疑的。html

先上一下OkHttp的官網瞅下:git

看一下它的簡單用法:github

好,我們仍是以上一節【http://www.javashuo.com/article/p-azwuqyvf-bx.html】獲取github的倉庫爲例,簡單用一下OkHttp,以下:web

首先增長OkHttp的依賴:api

而後來訪問這個接口「https://api.github.com/users/webor2006/repos」爲例,看下OkHttp是如何來請求的:瀏覽器

運行一下:服務器

其實緣由是在build中木有指定Java的編譯版本爲Java8致使,由於OkHttp庫中現在用到了Lambda表達式,因此指定一下:網絡

再次運行:框架

ok,關於用法就到這,關於OkHttp的具體使用這裏就不詳細贅述啦,用一用就會了,重點是分析它的原理機制,直接開始看源碼了,首先先看下它:ide

其中Request又是採用Builder模式來構建的,真的是無處不見Builder模式:

點進去看一下源碼:

能夠發現是由RealCall來返回的,很明顯RealCall是Call的具體實現,因此點擊RealCall.newRealCall看下它的源碼:

其中爲啥第二個參數名叫originalRequest,也就是在以後會對這個Request進行轉型,因此這是一個最初始的狀態;第三個參數涉及到了WebSocket,那啥叫WebSocket呢,百度百科一下:

也就是說它實際上是HTTP的一種擴展,正常的HTTP服務器端是不可能主動給客戶端消息推送的,而WebSocket通常是炒股交易的軟件會用,由於消息會比較實時,一般軟件 是不會用到它的,瞭解一下既可。

接下來再繼續:

那這個eventListener有啥用呢?HTTP請求過程當中有各類狀態,如TCP鏈接、SSL鏈接、請求等狀態,它主要是用來記錄這些狀態用的,不重要,貌似這個方法比較簡單,接着來就須要分析一下它了:

走進去:

很明顯它的實現應該是在RealCall裏面以下:

分析核心代碼,首先調用eventListener中的一個狀態方法:

而後接下來一句就是核心代碼了:

這句代碼涉及到三個東東:dispatcher()、enqueue()、AsyncCall,因此分別來了解一下:

dispatcher():

那瞅一下Dispatcher類:

而它用到了線程池:

假如想要全部的請求一個個前後執行,那隻要將上面的max設置爲1既可。

enqueue():

接下來則看一下它的源碼:

這裏有三個隊列定義於Dispatcher類中,以下:

因此說Dispatcher類就是一個任務調度類。

AsyncCall:

enqueue方法參數中還涉及到此類,先來瞅一下:

貌似對於Runnable應該裏面會有一個run()方法吧,可AsyncCall中木有看到,如它的結構所示:

那有可能定義在它的父類NamedRunnable中,因此瞅一下:

final class AsyncCall extends NamedRunnable {
    private final Callback responseCallback;
    private volatile AtomicInteger callsPerHost = new AtomicInteger(0);

    AsyncCall(Callback responseCallback) {
      super("OkHttp %s", redactedUrl());
      this.responseCallback = responseCallback;
    }

   ...

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

那這裏面應該就是整個請求處理的關鍵,下面來搞定它:

不過這裏暫且不深刻分析它,先來總結一下enqueue()的整個流程:它會去調用Dispather.enqueue()方法,而這個方法會生成一個AsyncCall對象,最終會執行AsyncCall.execute()方法,而後最終再真正請求給出結果反饋。

另外對於OkHttp的經典使用除了enqueue()異常請求以外,還有一個execute()方法,它是同步的,也來大體瞅一下它的實現:

也就是enqueue()和execute()就是是不是同步的區別而已。

好,接下來則須要研究兩個東東了:

一是OkHttpClient在實用角度(理解OkHttp跟Http的關係)須要理解一下它的配置項;

二是上面提到的攔截鏈。

好,先來瞅一下OkHttpClient這個類:

這個就是客戶端告訴OkHttp支持HTTP的版本,就像瀏覽器能支持HTTP1.0、HTTP1.2,這個屬性就是幹這個用的。

因此這個是告訴OkHttp是否支持HTTP,仍是支持HTTPS,而HTTPS要怎麼支持,也就是對於HTTP的底層支持OkHttp所有都實現了,這就能夠看出跟Retrofit的一個區別了,好,繼續!

關於這倆在以後再詳說,跟攔截器鏈有關,往下繼續:

這個還記得麼,回顧一下:

也就是它是用來建立eventListener的東東,往下:

關於它的使用場景在以前HTTPS中已經學習過了,這裏簡單點擊瞅一下:

它是JDK中的接口,其中OkHttp的實現以下:

就不細看了,繼續往下看:

對於有自簽名的需求用它是很是合適的,這裏來簡單瞭解一下它的用法,文檔上有描述,以下:

我們來貼過來試一下:

其中增長了一個驗證的選項,這樣就不會使用本地根證書進行驗證了,就會使用我們本身的驗證方式了,以下:

此時確定會驗證失敗,運行以下:

03-15 23:01:48.172 24248-24267/com.okhttpstudy.study E/cexo: Failed!!!Certificate pinning failure!
      Peer certificate chain:
        sha256/uL6J3XldY7njtfGsRP7HZgYsrNPrDZXpG7kT7wfg1m0=: CN=www.baidu.cn,OU=service operation department,O=BeiJing Baidu Netcom Science Technology Co.\, Ltd,L=Beijing,C=CN
        sha256/5kJvNEMw0KjrCAu7eXY5HZdvyCS13BbA0VJG1RSP91w=: CN=DigiCert SHA2 Secure Server CA,O=DigiCert Inc,C=US
        sha256/r/mIkG3eEpVdm+u/ko/cwxzOMo1bk4TyHIlByibiA5E=: CN=DigiCert Global Root CA,OU=www.digicert.com,O=DigiCert Inc,C=US
      Pinned certificates for baidu.com:
        sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=

其會在錯誤中會將此網站的全部簽名信息都會列出來,此時要想自簽名讓其經過驗證,則將錯誤提示的三個sha值添加到okhttpclient上,以下:

這個當有自簽名的需求時仍是比較好用滴。再繼續:

那具體是怎麼用它的呢?

簡單瞅下它:

至此整個OkHttpClient的字段都過了一遍,很明顯經過了解這些字段的含義能知道OkHttp底層作了好多事,清楚的能看到處處有http的身影。

接下來則須要研究Okhttp最核心的攔截器鏈啦,以下:

點進入瞅瞅它的實現:

其中首先是準備各類interceptor,以下:

接着將其生成一個InterceptorChain對像,以下:

用圖來表達一下攔截器鏈:

最後執行攔截器鏈的proceed(),此時實際上是這麼個過程:

爲啥要一個鏈呢?這裏能夠舉一個漢堡相關的例子用來理解:有人打電話進來訂了漢堡的餐,而後店老闆將漢堡作好了而且給負責分發的員工,接着送餐員到店取了漢堡騎着電動送到訂餐人的家裏,而後送餐員將漢堡給了訂餐的人,與此同時訂餐人將錢給了送餐員, 送餐員又拿着錢回到了店裏,並從中拿了1塊錢的提成,而後將剩下的錢所有交給了負責分發的店員,而後店員又將錢交給了店長,這就是一個鏈,店長是鏈的起點【作漢堡並分配給店員】和終點【從店員中拿到錢】。當一個事件比較複雜的時候採用鏈的方式會職責清晰,每個鏈則就是攔截器。

其中關於proceed()這裏仍是以上面舉的漢堡的店長爲例進一步闡述一下:其實對於店長的proceed()過程就是「作漢堡並分發給店員--->等待收錢---->收錢以後裝進本身的褲腰袋」,而結合圖的第一個節點表示店長的鏈,其過程實際上是這樣:

此時店長proceed()了,則會將連接給下一個接點,也就是分發的店員,以下:

接着通過漫長的等待,最終會從店員中收到錢,此時節點就會回到了以下位置:

用更加形象一點的圖來表示整個proceed()的過程,其實有點像遞歸:

從中能夠看到鏈上的每個結點都執行proceed()以後則就行成了一個一去一回的完整鏈的過程。

大概理解攔截鏈以後,接下來則細看一下具體的各個鏈,對於OkHttp的鏈,有兩個能夠自動配置的,以下:

這個先放一下,先來看一下其它具體的攔截器

RetryAndFollowUpInterceptor:

第一個是它:

重試及重定向攔截器,點進去,對於每個攔截器主要是看它的intercept()方法,因此:

其裏面作的事情基本上如咱們上面理論所說會有以下三件事件【OkHttp全部鏈的工做方式都遵循此原則,因此理解它很是之重要!】:

一、最初的準備(準備漢堡並交給店員)。

其中瞅一下StreamAllocation:

基本上這個攔截器前置工做比較簡單,重點是後置工做。

二、等待結果(等待收錢)。

接着執行proceed():

此時就會等待它以後的全部節點都處理完以後,此proceed()纔會返回結果。

三、處理結果(將錢裝自本身口袋)。

也就是proceed()以後的則是處理結果,具體來瞅下此攔截器的後置處理:

因此瞅一下recover方法:

若是符合重試條件,那麼直接再次循環:

其它的重試處理也相似:

最終:

BridgeInterceptor:

點進來瞅下,能很清楚的看到http的影子,以下:

好,仍是按以前的三步驟來分析:

一、最初的準備(準備漢堡並交給店員)。

基本全是加頭信息:

 

也就是OkHttp自身就已經支持gzip壓縮解壓縮,也就是開發者徹底透明,具體解的地方在:

這個是須要開發者本身定義的,由於CookieJar默認是空實現的,以下:

其大體用法能夠這樣寫:

二、等待結果(等待收錢)。

固然就是執行proceed()方法嘍:

三、處理結果(將錢裝自本身口袋)。

基本上作解工做,在proceed()以後:

CacheInterceptor:

比較簡單,就直接過了。

ConnectInterceptor【真正跟http或https進行交互了】:

點進去瞅下細節,主要是看流程,由於這個流程可以讓咱們清楚的發現跟TCP和SSL的創建過程:

好,目前TCP的鏈接已經看到了,那SSL的鏈接呢?得回到上一層,這:

因此此攔截器的做用就是用來創建TCP+SSL鏈接用的,而它木有後置工做:

CallServerInterceptor:

由於它是最後一個攔截器了,因此木有proceed()了,作完事情直接返回就ok了,它都是作的實質工做,下面也來分析其關鍵代碼:

咱們看http1的就成:

也就是經過TCP來往服務器創建通訊了,接着來處理response並返回:

最後就只剩自定義的攔截器木有看了,這個比較簡單,首先能夠配置一個本身的攔截器,以下:

對於我在公司的項目一般會用這個攔截器來打印一下日誌,或者傳一些公共頭信息之類的,總之比較好用。

那對於這兩個自定義的攔截器,有啥區別呢?

因此若是想直接http數據時則須要用networkInterceptors,通常用不到它,主要是用上面的自定義攔截器。

到此,整個完整的鏈都分析完了,能夠看到OkHttp是整個接管了整個Http的工做,從TCP鏈接的創建,TLS鏈接的創建,HTTP報文相關的處理,而Retrofit是利用Okhttp來實現的,只是API作了不少的容錯。

相關文章
相關標籤/搜索