Retrofit2源碼解析——網絡調用流程(下)

Retrofit2源碼解析系列

  • Retrofit2源碼解析(一)
  • Retrofit2源碼解析——網絡調用流程(上)

本文基于Retrofit2的2.4.0版本

implementation 'com.squareup.retrofit2:retrofit:2.4.0'
復制代碼

上次我們分析到網絡請求是通過OkHttpCall類來完成的,下面我們就來分析下OkHttpCall類。

final class OkHttpCall<T> implements Call<T> {...@Overridepublic void enqueue(final Callback<T> callback) {checkNotNull(callback, "callback == null");okhttp3.Call call;Throwable failure;synchronized (this) {if (executed) throw new IllegalStateException("Already executed.");executed = true;call = rawCall;failure = creationFailure;if (call == null && failure == null) {try {//調用createRawCall創建OkHttp3的Callcall = rawCall = createRawCall();} catch (Throwable t) {throwIfFatal(t);failure = creationFailure = t;}}}...call.enqueue(new okhttp3.Callback() {@Overridepublic void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {Response<T> response;try {//解析返回的結果response = parseResponse(rawResponse);} catch (Throwable e) {callFailure(e);return;}try {callback.onResponse(OkHttpCall.this, response);} catch (Throwable t) {t.printStackTrace();}}@Overridepublic void onFailure(okhttp3.Call call, IOException e) {callFailure(e);}private void callFailure(Throwable e) {try {callback.onFailure(OkHttpCall.this, e);} catch (Throwable t) {t.printStackTrace();}}});}...
}
復制代碼

OkHttpCall的enqueue方法主要干了2件事,一個是創建OkHttp3的Call用于執行網絡請求;另一個是解析返回的結果并回調。下面我們來看看創建OkHttp3的Call的過程

//OkHttpCall.class
private okhttp3.Call createRawCall() throws IOException {okhttp3.Call call = serviceMethod.toCall(args);if (call == null) {throw new NullPointerException("Call.Factory returned null.");}return call;
}
復制代碼

可以發現是通過serviceMethod的toCall方法來創建的

//ServiceMethod.class
okhttp3.Call toCall(@Nullable Object... args) throws IOException {RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl, headers,contentType, hasBody, isFormEncoded, isMultipart);...for (int p = 0; p < argumentCount; p++) {handlers[p].apply(requestBuilder, args[p]);}//最后調用OkHttpClient的newCall方法返回Callreturn callFactory.newCall(requestBuilder.build());
}
復制代碼

ServiceMethod的toCall方法也是通過OkHttpClient的newCall方法來返回Call的。

在我們通過OkHttpClient請求得到結果后,我們還需要將返回的結果Response解析成我們接口需要的實體類型,這就需要用到我們在創建Retrofit時設置的ConverterFactory了,比如GsonConverterFactory。

//OkHttpCall.class
Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {ResponseBody rawBody = rawResponse.body();rawResponse = rawResponse.newBuilder().body(new NoContentResponseBody(rawBody.contentType(), rawBody.contentLength())).build();...ExceptionCatchingRequestBody catchingBody = new ExceptionCatchingRequestBody(rawBody);try {//通過serviceMethod的toResponse方法解析T body = serviceMethod.toResponse(catchingBody);return Response.success(body, rawResponse);} catch (RuntimeException e) {catchingBody.throwIfCaught();throw e;}
}
復制代碼

OkHttpCall的parseResponse方法調用的是serviceMethod的toResponse方法來解析返回的結果。

//ServiceMethod.class
R toResponse(ResponseBody body) throws IOException {return responseConverter.convert(body);
}
復制代碼

在ServiceMethod中最后調用responseConverter的convert方法來轉換返回的結果。這個responseConverter和上面分析的CallAdapter的確定過程一樣,也是在ServiceMethod的build方法中,通過調用retrofit的requestBodyConverter方法遍歷我們傳入的ConverterFactory,直到找到合適的。

//Retrofit.class
public <T> Converter<T, RequestBody> requestBodyConverter(Type type,Annotation[] parameterAnnotations, Annotation[] methodAnnotations) {return nextRequestBodyConverter(null, type, parameterAnnotations, methodAnnotations);
}public <T> Converter<T, RequestBody> nextRequestBodyConverter(@Nullable Converter.Factory skipPast, Type type, Annotation[] parameterAnnotations,Annotation[] methodAnnotations) {...int start = converterFactories.indexOf(skipPast) + 1;for (int i = start, count = converterFactories.size(); i < count; i++) {Converter.Factory factory = converterFactories.get(i);Converter<?, RequestBody> converter =factory.requestBodyConverter(type, parameterAnnotations, methodAnnotations, this);if (converter != null) {//noinspection uncheckedreturn (Converter<T, RequestBody>) converter;}}...
}
復制代碼

需要注意的是在創建Retrofit時默認添加了一個BuiltInConverters,這個是Retrofit為我們提供一個默認的responseConverter,它主要處理的是返回類型是ResponseBody和Void的情況。

final class BuiltInConverters extends Converter.Factory {@Overridepublic Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,Retrofit retrofit) {if (type == ResponseBody.class) {return Utils.isAnnotationPresent(annotations, Streaming.class)? StreamingResponseBodyConverter.INSTANCE: BufferingResponseBodyConverter.INSTANCE;}if (type == Void.class) {return VoidResponseBodyConverter.INSTANCE;}return null;}...
}
復制代碼

因為我們一般返回值類型都是具體的實體類型,所以我們需要添加自己的responseConverter,一般也就是GsonConverterFactory了。

至此,網絡調用的后半部分流程也清楚了:

我們調用Call對象的enqueue方法發起異步請求時,實際上調用的是OkHttpCall對應的enqueue方法。OkHttpCall會先調用ServiceMethod類的toCall方法利用OkHttpClient的newCall方法創建OkHttp3的call對象,然后利用這個call對象執行具體的網絡請求。在網絡請求返回成功以后會調用ServiceMethod類的toResponse方法利用我們設置的responseConverter將返回結果轉換成我們需要的類型,然后通過我們設置的回調或是默認的回調方法,將結果回調回主線程,從而完成整個請求過程。

總結

Retrofit2的網絡調用的整個流程我們已經分析完了。通過這次分析,我們可以看到Retrofit2中最主要的就是3個類:Retrofit、ServiceMethod和OkHttpCall。這三個類指責明確,相互配合共同完成整個網絡調用的流程。

(1)Retrofit負責供外部初始化和定制,保存CallAdapter的列表和ResponseConverterFactory列表。

(2)ServiceMethod對應每一個接口方法的信息,包括解析注解和參數等,同時它也是連接Retrofit和OkHttpCall的橋梁。ServiceMethod中保存著當前接口對應方法所需要的CallAdapter和ResponseConverter。利用CallAdapter將OkHttpCall轉換成接口需要的類型,供接口調用。利用toResponse方法讓OkHttpCall調用ResponseConverter解析網絡請求返回的結果。

(3)OkHttpCall則是用來執行具體網絡請求。Retrofit2沒有直接使用OkHttp3的Call接口,而是有自己的Call接口。在OkHttpCall內部通過組合的方法持有OkHttp3的Call接口,并通過ServiceMethod的toCall方法得到OkHttp3的call來進行網絡請求,減少對OkHttp3的耦合。


                    歡迎關注我的微信公眾號,和我一起每天進步一點點!
復制代碼

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/253530.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/253530.shtml
英文地址,請注明出處:http://en.pswp.cn/news/253530.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

spring EL 實現ref的效果

之前學習basic的時候有個疑問就是不知道如何實現bean中引用其他的bean的屬性&#xff0c;當時是用ref來實現對其他bean的引用&#xff0c;但是ref必需引用的是一個常量。所以這種方式來實現對其他bean中的屬性的引用是不合理的。 當我看到Spring Expression Language時發現原來…

2021手機CIS技術趨勢總結

手機攝像頭CIS&#xff08;CMOS圖像傳感器&#xff09;自從突破1億像素以后&#xff0c;再談像素數量增大&#xff0c;似乎已經很難讓市場產生激烈反應了。這兩年電子工程專輯對于手機攝像頭CIS&#xff0c;以及更多領域不同類型的圖像/視覺傳感器&#xff08;如ToF、基于事件的…

關于Unity中NGUI的背包實現之Scrollview(基于Camera)

基于UIPanel的scrollview實現方式在移動設備上的性能不如基于camera的方式。因為UIPanel的scrollview實現方式要渲染很多的道具圖&#xff0c;性能自然就降低了。如果是用第二個攝像機camera的方式&#xff0c;物體并沒有動&#xff0c;只是拖動第二個攝像機攝像機&#xff0c;…

YUV422/420 format

(在本文中&#xff0c;U 一詞相當于 Cb&#xff0c;V 一詞相當于 Cr。) YUV422 format as shown below 4:2:2 表示 2:1 的水平取樣&#xff0c;沒有垂直下采樣 YUV420 format as shown below4:2:0 表示 2:1 的水平取樣&#xff0c;2:1 的垂直下采樣. YUV4:2:0并不是說只有U&…

vue部署問題

history模式配置后刷新404的解決辦法! 第一種 nginx配置 在usr/local/nginx/conf/vhost 下 域名.conf配置文件修改或添加 第一種方案server {##在server下添加或在location里面添加以下代碼location / {if (!-e $request_filename) {rewrite ^(.*)$ /index.html?s$1 last…

位域

有些信息在存儲時&#xff0c;并不需要占用一個完整的字節&#xff0c; 而只需占幾個或一個二進制位。例如在存放一個開關量時&#xff0c;只有0和1 兩種狀態&#xff0c; 用一位二進位即可。為了節省存儲空間&#xff0c;并使處理簡便&#xff0c;C語言又提供了一種數據結構&a…

數字后端——ECO

目錄 一、概述 二、ECO分類 1、按時間節點 1&#xff09;流片前的ECO 2&#xff09;流片過程的ECO 3&#xff09;流片后的ECO 2、按網表是否改變 1&#xff09;功能ECO 2&#xff09;時序ECO 三、ECO處理內容 1、設計規則違例 1&#xff09;提升標準單元驅動力 2…

Uncaught TypeError: Cannot read property 'length' of null錯誤怎么處理?

Uncaught TypeError: Cannot read property length of null 錯誤怎么處理&#xff1f; 1.可能是返回的datagrid數據格式有問題&#xff0c;比如{"total":0,"rows":null}&#xff0c;改為{"total":0,"rows":"[]"}就可以了 if…

電視百科常識 九大視頻接口全接觸

1 射頻 天線和模擬閉路連接電視機就是采用射頻&#xff08;RF&#xff09;接口。作為最常見的視頻連接方式&#xff0c;它可同時傳輸模擬視頻以及音頻信號。RF接口傳輸的是視頻和音頻混合編碼后的信號&#xff0c;顯示設備的電路將混合編碼信號進行一系列分離、解碼在輸出成像。…

tracert路由檢測命令使用方法

很多客戶網站無法訪問的時候都會第一時間懷疑是虛擬主機有問題了&#xff0c;其實大多時候網站無法訪問和很多因素相關&#xff0c;包括自己的網絡、計算機設置、省際路由等等&#xff1b; 那么這里我就簡單講下如何利用DOS下的命令檢測你的計算機到服務器之間的路由是否通暢&a…

數字后端——物理單元介紹

物理單元&#xff08; physical cell&#xff09;指沒有邏輯功能但是具有物理實現功能的標準單元&#xff0c; 用于抑制芯片生產過程中的各類物理效應&#xff0c; 保證芯片生產后能夠正常工作 。硬核位置確 定后&#xff0c;需要插入物理單元消除影響芯片工作的物 效應&#x…

vue雙向數據綁定的原理

有關雙向數據綁定的原理 最近兩次面試的時候&#xff0c;被問到了vue中雙向數據綁定的原理&#xff0c;因為初學不精&#xff0c;只是使用而沒有深入研究&#xff0c;所以答不出來。之后就在網上查找了別人寫的博客&#xff0c;學習一下。 下面是博客園一篇博客&#xff0c;以及…

求職網站總結

最近忙著要找份工作。畢業半年多就辭職&#xff0c;也是尷尬。 這里記錄一些求職網站和找工作的一些經驗。主要參考了三個知乎問題&#xff1a;怎么在互聯網上找工作&#xff1f;&#xff0c;招聘網站&#xff0c;哪個靠譜&#xff1f;和哪個求職網站&#xff08;app&#xff0…

FTP命令解析

FTP命令詳解FTP命令是Internet用戶使用最頻繁的命令之一&#xff0c;不論是在DOS還是UNIX操作系統下使用FTP&#xff0c;都會遇到大量的FTP內部命令。熟悉并靈活應用FTP的內部命令&#xff0c;可以大大方便使用者&#xff0c;并收到事半功倍之效。   FTP的命令行格式為&…

深入Java內存模型

你可以在網上找到一大堆資料讓你了解JMM是什么東西&#xff0c;但大多在你看完后仍然會有很多疑問。happen-before是怎么工作的呢&#xff1f;用volatile會導致緩存的丟棄嗎&#xff1f;為什么我們從一開始就需要內存模型&#xff1f; 通過這篇文章&#xff0c;讀者可以學習到足…

Matlab 使用GPU加速 轉載

在matlab中使用GPU加速&#xff0c;來加速矩陣運算。 首先如前面所說&#xff0c;并不是所有GPU都能在maltab中進行加速的&#xff0c;貌似只有NVDIA的顯卡可以吧。 硬件&#xff1a;GeForce GTX 980 軟件&#xff1a;Matlab 2015a &#xff08;Matlab 2012以后的版本才帶有GP…

數字后端——可制造性設計

隨著集成電路制造工藝技術的迅速發展&#xff0c;集成電路集成度迅速攀升&#xff0c;制造流程及工藝步驟日趨復雜&#xff0c;工藝尺寸也在不斷縮小。集成電路可制造性設計&#xff08;Design For Manufacturability,DFM&#xff09; 以直接提升集成電路芯片的良品率及降低芯片…

Cloudstack安裝(二)

Cloudstack安裝 官方文檔參考&#xff1a; http://docs.cloudstack.apache.org/projects/cloudstack-installation/en/4.9/qig.html#environment Cloudstack主要分Management和Agent兩部分。 系統版本&#xff1a;CentOS 6.8 Management&#xff1a; cpu1&#xff0c;ram 2048M…

Pycharm 輸出中文或打印中文亂碼現象的解決辦法

轉載地址&#xff1a;https://www.cnblogs.com/Bro-Young/p/5920884.html 1. 確保文件開頭加上以下代碼&#xff1a; 1 # -*- coding:utf-8 -*- 還可以加上 1 import sys 2 reload(sys) 3 sys.setdefaultencoding(utf-8) 確保以下。 如果還是沒有解決中文亂碼&#xff0c;那么進…

計算機系統結構——概述

計算機的實現包括兩個方面&#xff1a;組成和硬件。組成一詞包含了計算機設計的高階內容&#xff0c;例如存儲器系統&#xff0c;存儲器互連&#xff0c;設計內部處理器 CPU &#xff08;中央處理器——算術、邏輯、分支和數據傳送功能都在內部實現&#xff09;。有時也用微體系…