前面介紹了網絡的基礎知識,這篇主要從OKHttp源碼角度來分析Http。OKHttp是一個優秀的網絡請求框架,有以下特點:
- 支持HTTP2/SPDY
- Socket自動選擇最好路線,並支持自動重連
- 擁有自動維護的Socket連接池,減少握手次數
- 擁有隊列線程池,輕鬆寫併發
- 擁有Interceptors輕鬆處理請求與響應(比如透明GZIP壓縮)
- 實現基於Headers的緩存策略
基本使用
同步請求
同步的Get請求
異步請求
異步的Get請求
源碼分析
我們從OKHttp的初始化開始分析。
OkHttpClient
新建一個OkHttpClient對象
<code>OkHttpClient client =new
OkHttpClient();/<code>
構造函數聲明:
Builder模式構造:
聲明瞭很多屬性,具體含義,等後面用到在具體介紹。
請求流程
請求流程可分為同步和異步,大體的請求流程如下圖所示:
OKHttp流程
同步請求流程
<code>client
.newCall
(request
).execute
();/<code>
newCall返回的是RealCall,上面代碼實際上執行的是RealCall的execute方法。
- executed判斷Call對象是否已經執行,每個Call對象只能執行一次
- client.dispatcher()返回Dispatcher對象,任務核心調度類,是OKHttp中最重要類之一, executed方法把該線程添加到同步線程隊列synchronized void executed(RealCall call) { runningSyncCalls.add(call); }
- getResponseWithInterceptorChain()獲取HTTP請求結果,並會進行一系列攔截操作
- client.dispatcher().finished(this)執行完畢操作
執行完畢後,會把線程從同步線程隊列中移除:
異步請求流程
RealCall的enqueue方法:
- executed含義和同步請求一樣,表示請求只能執行一次
- client.dispatcher().enqueue(new AsyncCall(responseCallback));,會生成一個AsyncCall對象,並把它加入到readyAsyncCalls線程隊列中,等待執行
AsyncCall是RealCall的內部類,並且是NamedRunnable線程類,具體執行方法:
- getResponseWithInterceptorChain()獲取HTTP請求結果,並會進行一系列攔截操作
- client.dispatcher().finished(this);這個方法很重要,和同步方法中調用類似,但是異步的流程則完全不同
finish方法:
異步流程中,promoteAndExecute方法:
會遍歷異步等待線程隊列,並對正在執行的異步線程隊列進行最大請求size,以及每個host最大請求size進行檢查。把異步等待線程放到正在執行線程隊列中,並在等待線程隊列中刪除該線程,這樣就把等待線程變成正在執行線程。
Dispatcher
任務調度核心類,這個類,其實在同步和異步請求流程中已經介紹過,其最重要功能是負責請求的分發。Dispatcher在OKHttpClient的Builder中被初始化:
- maxRequests:最大請求併發請求數64
- maxRequestsPerHost:每個主機的最大請求數5
- executorService:線程池
- readyAsyncCalls:異步等待線程隊列
- runningAsyncCalls:正在運行的異步線程隊列
- runningSyncCalls:正在運行的同步線程隊列
線程池executorService的聲明:
- 核心線程數為0,表示線程在空閒時不會被保留,等待一段時間後停止
- 最大線程數Integer.MAX_VALUE,基本上就是可以創建線程無上限
- keepAliveTime為60s,表示如果線程空閒時,最多隻能存活60s
綜合上訴,在OKHttp中,設置了不設上限的線程,不保留最小線程,線程空閒時,最大存活時間為60s,保證I/O任務中高阻塞低佔用的過程,不會長時間卡在阻塞上。並通過maxRequests和maxRequestsPerHost來控制併發最大請求數。
攔截器
在同步和異步請求中,具體的執行過程中都會調用到getResponseWithInterceptorChain方法,該方法添加了一系列的攔截器,它在OKHttp整理流程中處於非常重要的地位,
流程
方法實現:
默認添加的攔截器:
- RetryAndFollowUpInterceptor:負責失敗重試以及重定向
- BridgeInterceptor:負責把用戶構造的請求轉換為發送到服務器的請求、把服務器返回的響應轉換為用戶友好的響應
- CacheInterceptor:負責讀取緩存直接返回、更新緩存
- ConnectInterceptor:負責和服務器建立連接
- CallServerInterceptor:負責向服務器發送請求數據、從服務器讀取響應數據
這是典型的責任鏈模式,通過Interceptor,把Request轉換為Response,每個Interceptor都有各自的責任和邏輯。
開發者可以自己定義Interceptor,在最開始或者發送請求前,對Request和Response進行處理。
HTTP實現
OKHttp中實現HTTP主要是在ConnectInterceptor和CallServerInterceptor。ConnectInterceptor建立服務器之間的連接,CallServerInterceptor發送請求和讀取響應。OKHttp請求一個URL的流程:
根據請求的URL,createAddress方法會創建一個Address,用於連接服務器檢查address和routes,是否可以從ConnectionPool獲取一個連接如果沒有獲取到連接,會進行下一個路由選擇(routeSelector),並且重新嘗試從ConnectionPool獲取一個連接。重試還是獲取不到,就會重新創建一個連接(RealConnection)獲取連接後,它會與服務器建立一個直接的Socket連接、使用TLS安全通道(基於HTTP代理的HTTPS),或直接TLS連接發送HTTP請求,並獲取響應
ConnectInterceptor
在請求發送前的邏輯,都是ConnectInterceptor中實現,ConnectInterceptor的intercept,這個是3.14.2版本源碼,和以前多版本稍微有些區別。
Exchange可以傳輸HTTP請求和響應,並管理連接和事件。newExchange方法調用:
find方法會最終執行ExchangeFinder的findConnection方法,在發送HTTP請求之前的邏輯,都是這個方法中實現。
HTTP 的連接主要是result.connect方法:
在 for 循環中檢查這個連接是否是隧道協議連接。connectSocket連接socket,establishProtocol根據HTTP協議版本進行連接處理。重點分析下connectSocket方法:
使用 Okio,封裝了Socket的讀寫操作, 建立連接後,就可以發送請求和獲取響應。
CallServerInterceptor
CallServerInterceptor的intercept()方法裡負責發送請求和獲取響應。具體操作都是通過Exchange來執行,Exchange通過各個功能模塊再進行分發處理。通過 Socket 發送 HTTP消息,會按照以下聲明週期:
- writeRequestHeaders發送 request Headers
- 如果有 request body,就通過 Sink 發送request body,然後關閉 Sink
- readResponseHeaders獲取 response Headers
- 通過Source讀取 response body,然後關閉 Source
writeRequestHeaders
Exchange 調用writeRequestHeaders方法
實際執行的方法codec實現類Http1ExchangeCodec(前面根據HTTP協議版本選擇)的writeRequest方法
readResponseHeaders
讀取響應頭部,Http1ExchangeCodec的readResponseHeaders方法:
StatusLine解析HTTP版本信息,readHeaders()讀取response header 信息。
response body
解析 response body 內容:
如果不是websocket,調用Exchange的openResponseBody方法:
獲取返回的 body,通過 Source 轉換為需要的數據類型,ResponseBody提供的 string(),轉換為 String 類型
通過上述的分析,OKHttp是通過Okio操作Socket實現了Http協議,憑藉高效的性能,Android系統從4.4版本開始,HTTP的實現已經替換為OKHttp。
參考
- OKHttp源碼解析(一)—初階
- 拆輪子系列:拆 OkHttp