讀okhttp源碼(二)

上一篇我們從okhttp get請求的例子說起,談了很多,這次我們來看看okhttp post請求,先來看一下示例代碼:

public static final MediaType JSON
    = MediaType.get("application/json; charset=utf-8");

OkHttpClient client = new OkHttpClient();

String post(String url, String json) throws IOException {
  RequestBody body = RequestBody.create(JSON, json);
  Request request = new Request.Builder()
      .url(url)
      .post(body)。
      .build();
  try (Response response = client.newCall(request).execute()) {
    return response.body().string();
  }
}

首先我們來看一下MediaType類,它是根據RFC2045文件定義,用來描述http請求和響應體的內容類型,其構造方法是私有的,要構造它的實例,有兩個靜態方法,都是接收一個string參數,解析這個string來構造MediaType類實例,然后我們就可以調用實例方法獲取type,subtype,charset,比如上面的"application/json; charset=utf-8",type就是"application",subtype就是"json",charset自然是utf-8。

然后我們看一下post方法,首先構造一個RequestBody方法,之前我們說過這是一個抽象類,這里用到了create靜態方法構造,這個方法里先是檢查contentType和其charset實例是否為null,根據條件設置默認值或者追加數據,然后會調用接收byte數組的重載方法,然后又會調用另一個附帶offset參數的重載方法,在這個方法里,確認參數數值合法之后,創建一個RequestBody的匿名實現,contentType方法直接返回參數contentType,contentLength方法返回byte數組的長度,至于writeTo方法,則是直接調用sink的write方法。RequestBody里還有create的其他重載方法,這里就不介紹了。

基本上post請求和get請求,就是構造Request時在Builder上調用了post方法,這個方法的實現,就是先進行條件檢查,get請求則不能有body,post請求則不能沒有body,然后就賦值request內部域method和body,后面的過程,和上一篇get請求一樣,我這里也跳過了。

看完這些,接著我們就詳細看一下okhttp所有的代碼,我用intellij idea生成了okhttp module的源碼的javadoc,看一下所有的類,感興趣的再深入代碼了解。

AndroidPlatform類,是Platform的子類,之前講到調用RealConnection的connectSocket方法時,會調用Platform.get().connectSocket,所以我這里看一下AndroidPlatform的connectSocket實現。這里很簡單,就是調用socket.connect方法,傳遞一個SocketAddress的類實例,以及一個int類型的connectTimeout,Socket和SocketAddress都是java平臺類,因為SocketAddress是個抽象類,這里就使用java平臺提供的一個實現類:InetSocketAddress,傳遞其實例以完成調用,然后就是一些異常處理。

Cache類,之前提到其內部使用DiskLruCache類,我們來看看這個類,它是okhttp實現的基于文件系統的緩存。其內部使用日志文件來管理管理狀態,一開始是頭信息,包括緩存版本號,應用版本號等等,然后就是緩存條目的狀態的記錄,有DIRTY,CLEAN,REMOVE,READ,詳細的可以查看類內部注釋。DiskLruCache的構造方法是包級私有的,另外還提供了create靜態方法創建其實例,此方法比較簡單,就是檢查參數合法性,然后調用構造方法,構造方法里也就是設置DiskLruCache的內部域,至于真正的創建緩存目錄、日志文件,則是在調用get,edit等方法時調用initialize方法初始化的。DiskLruCache的內部域lruEntries是一個LinkedHashMap<String,Entry>,這里的Entry包含緩存數據,緩存數據超過限制時,按照lruEntries的values方法返回的數據順序來刪除數據。

后面有很多類是涉及到http2,我也就是粗略看了一下,等需要的時候再深入研究也不遲。然后我看到在okhttp里,還定義了WebSocket接口,以及其實現類RealWebSocket。先來看一下RealWebSocket的connect方法。在request的header里追加一些針對web socket的數據之后,調用Internal.instance.newWebSocketCall(client, request)實際也就是RealCall.newRealCall(client, originalRequest, true)方法,差別就是第三個參數forWebSocket設置為了true,然后就調用RealCall的enqueue(Callback)方法,這個方法之前講過,實際也就在內部thread里走整個網絡請求,在callback的onResponse(Call call, Response response)執行成功的回調中,我們看到,先得到streamAllocation實例,然后調用streamAllocation.connection().newWebSocketStreams(streamAllocation)方法得到web socket的流,返回的對象類型是RealWebSocket的內部類Streams,然后就可以使用這個Streams創建WebSocketWriter和WebSocketReader,創建定時執行ping的任務,發送在連接前已排隊的消息,啟動循環接收消息,這樣connect方法就結束了。

webSocketReader類的方法調用跟realWebSocket在一個線程,所謂讀線程,而webSocketWriter的方法是在另外的線程執行的,由realWebSocket內部ScheduledExecutorService實例維護,所以webSocketWriter的很多調用會加鎖,而webSocketReader都沒有,另外這兩個類自身都是線程不安全的。

webSocketWriter和webSocketReader內部,都是解析由協議定義好的幀數據,分為控制幀和消息幀,WebSocketReader內部定義了FrameCallback接口,在讀取數據和關系時都由回調。另外這里還涉及到之前提到的定時ping任務,這也是通過寫入ping幀和讀取pong幀來保持會話。

okhttp的內部就先寫到這,后面有機會深入使用的時候,再來談談好了。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容