okhttp核心原理分析(1)


更多文章分類

文章思維導圖

前言

進行 okhttp 的核心源碼分析,必須要搞清楚 http 協議以及相關的網絡協議。這里只對協議容易混淆的地方進行說明。

首先我們要明確一點,要想讓兩台計算機進行通信,首先需要建立連接,也就是我們常說的三次握手。

計算機A 要想和計算機 B 進行通信,首先要知道計算機B 的IP 地址,知道 IP 地址后,就能訪問計算機B,而要和計算機B上的那個程序通信,這個時候就需要 TCP 的地址了,也就是端口號。有了這兩個信息,兩者就可以建立連接了。其實這個時候就可以進行通信了。體現在代碼中,就是通過 Socket 建立連接,然后進行讀寫操作。

這個時候也看出來了,所謂的 Socket 其實就是對 TCP/IP 建立連接通信的一種具體封裝,不同的語言代碼有不同的實現。有了 Socket 后,開發者就可以方便地進行網絡連接通信了。Socket 本身並不是 TCP/IP網絡協議中的協議

HTTP 協議就是在建立了 Socket 連接后規定了通信內容的格式,大家都遵從這個協議進行通信。

HTTP通信內容?

簡單來說,所謂的 HTTP 通信就是建立 Socket 連接,然后把通信內容拼接成符合 HTTP 報文的內容發送出去。

什么是 okhttp

有了前言的內容,我們就可以理解什么是 okhttp 了,所謂的 okhttp 就是通過代碼的方式實現了各種協議,將這些通信協議封裝起來,讓我們可以快速地用代碼來實現。

okhttp 好處

  • 支持 HTTP1、HTTP2、Quic以及 WebSocket

    之所以支持,是因為 okhttp 的源碼里面對這些協議的規則進行了實現。

  • 連接池復用底層 TCP連接,減少請求延時。

    建立 TCP 連接是需要時間的,okHTTP 源碼中對已經連接的 TCP,其實在代碼中的體現就是 Socket 進行了緩存,再次請求同一地址的時候就不用重復建立連接了,從而減少請求延時。

  • 無縫的支持 GZIP 減少數據流量

    其實這是 HTTP 協議的內容,HTTP 協議中可以在請求頭中規定是否支持數據壓縮,okhttp 就把這個請求頭封裝進去了,告訴服務器,我可以接受一個 GZIP 壓縮的數據報文。

  • 緩存響應數據減少重復的網絡請求

    這也是 HTTP 協議中定義的內容,有對應的字段表示

  • 請求失敗自動重試主機的其他 ip,自動重定向

    同樣也是 HTTP 協議中定義的內容。

可見所謂的這些好處,其實就是 okhttp 利用 HTTP 報文格式中規定的內容,然后進行處理,完成這些規定。如果沒有處理的話,任憑服務器發送來的報文是什么,如果統統不管的話,那也是沒用的。

因此再次說明 HTTP 協議只是規定了你我通信要發送的內容需要遵從什么樣的格式,至於我有沒有根據內容,實現對應的功能,那就不是 HTTP 協議的范圍了。

okhttp 使用

    // 步驟 1   
	OkHttpClient okHttpClient = new OkHttpClient.Builder()
                .readTimeout(60,TimeUnit.SECONDS)
                .connectTimeout(60,TimeUnit.SECONDS)
                .cache(new Cache(new File("xx"),1024))
                .build();
	// 步驟 2
        Request request = new Request.Builder()
                .url(AppConfig.URL.url_get)
                .build();
    // 步驟 3    
        Call call = okHttpClient.newCall(request);
   // 步驟 4    
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                
            }

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

            }
        });

okhttp 的使用一般就是上面 4 步。

第一步:創建 okhttpClient ,也就是一個客戶端,同時也是也 Call 的工廠,主要作用就是記錄一些配置內容,比如 : 連接超時時間、讀取超時時間、緩存地址等等這種配置。這個對象可以共用,不用每次都創建。

第二步:就是創建一個請求報文。

第三步:就是通過 Call 工廠 okhttpClient 和 請求報文,構建出一個准備好要執行的請求。通過 Call 來發起網絡請求。

第四步:發起網絡請求

雖然就這樣簡單的四步,但是代碼設計的非常好,首先做到了功能分離!單一職責,有三個類 OkhttpClientRequestCall 分別負責不同的職責,非常清晰。

核心原理分析

okhttp 整個大的流程核心就是一個分發器 Dispatcher 和 攔截器 interceptors 下面分別分析

Dispatcher

分發器用於執行我們網絡請求的異步任務,Dispatcher 有 4 個核心成員對象 :

  • ExecutorService

    線程池,用於執行異步任務

  • 三個隊列 Deque<AsynCall>

    三個隊列,分別是用於存儲 正在執行的異步任務、正在執行的同步任務、准備執行的異步任務

先看一張流程圖

Dispatcher 的整個執行流程就如上圖所示,下面來結合源碼分別介紹。

// 異步請求
call.enqueue(new Callback() );
// 這個時候會執行 RealCall 下面的方法

這個時候就進入 Dispatcher 分發器

重點來了

首先這個方法是個同步方法,有個判斷,判斷這個請求是放入 running 隊列還是 ready 隊列。

圖中的1 就是判斷條件,如果 running 隊列中的 call 小於最大請求數(默認 64)並且對同一地址的請求小於 最大主機請求數(默認5),這個時候就放入 running 隊列,直接交給線程池來執行 請求。否則加入 ready 隊列,等待請求。

然后看 executorService().execute(call) 這一步其實就是交給線程池執行,最終執行的是 AsyncCallexecute() 方法

注意這個方法是在子線程中執行的。

1: 是真正的觸發網絡請求,進入下一個核心點 “攔截器”。(后面講解)

2:可以看到 2 是在 finally 中執行的,也就是總是會執行到。

1:執行完畢后就把 call 從 runing 隊列中移除了,然后執行 2

這里會根據條件循環判斷 ready隊列中的 call 是否能添加到 running 隊列中執行。

到此整個分發器的執行流程就結束了!

總結

對於 Dispatcher 分發器核心點就是一個線程池、維持請求隊列。

添加一個請求的時候會判斷正在請求的數量,如果條件滿足就放入線程池執行,否則放入等待隊列,等待執行。

后面我們會繼續介紹下一個核心-----攔截器


免責聲明!

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



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