Retrofit 源碼解析

1. 功能介紹

1.1 Retrofit

Retrofit 是 Github 上面 squre 組織開發的一個類型安全的 Http 客戶端,它可以在 Java 和 Android 上面使用。Retrofit 將描述請求的接口轉換為對象,然后再由該對象去請求后臺。Retrofit 將請求對象化了。目前已經發布了 2.0beta 版本。

1.2 特點

Retrofit 主要有以下功能特點

  1. 將 Http 請求對象化,函數化。讓接口的函數代表具體請求。
  2. 利用注解的方式標記參數,將 HTTP 的請求方法,請求頭,請求參數,請求體等等都用注解的方式標記,使用起來非常方便。
  3. 支持 Multipart,以及文件上傳(file upload)。
  4. 直接將 Http 的 Response 轉換成對象。用戶可以根據 Response 的具體內容,更換轉換器,或者自己新建轉化器。
  5. Retrofit 默認使用 OkHttp 開源庫請求后臺,用戶也可以使用自定義的具體請求方式。方便擴展。
  6. 自帶提供了異步處理 Http 請求的方式。

1.3 簡單 Demo

這是一個簡單的例子,訪問httpbin網站。也可以看完整的Retrofit Demo 首先聲明一個 java 接口

使用方式

使用 httpbinService 獲取一個 Call,用來請求 HTTP 服務器。

因為接口返回的應該是一個 Call,用來請求后臺 HTPP 服務器,所以我們在聲明接口的似乎,返回參數應該是 Call<?>。由于 httpbin 返回的是一個 json 格式的數據,我們想要返回直接的自定義的模型數據,但是 retrofit 默認只會返回 ResponseBody,所以我們需要自己添加一個 GsonConverter 第三方庫。在 build.graddle 中的 dependencies 添加:

compile 'com.squareup.retrofit:converter-gson:2.0.0-beta2'

2. 總體設計

Retrofit 可以分為注解解析(Request 生成),請求執行,請求回調(異步處理),響應體轉化幾個部分。其中請求執行與請求回調可以算作一個部分,并且請求回調也可以沒有,Call 有直接執行的接口 execute。


  1. 首先由解析部分(這部分也是 Request 生成部分),利用注解(Annotation)解析接口文件,將接口方法解析好,每個方法生成一個 Request。
  2. 然后利用 Call 部分執行 Request。Retrofit 使用的是 okHttp 來請求,程序中將 Retrofit Request 轉化為 OKHttp 開源庫的 Request,轉由 OkHttpClient 執行。
  3. 在 Request 執行完后,得到 Response,使用 Converter 轉化 Response 為用戶需要的對象。比如將 json 格式的數據,利用 gson 轉化為具體的 Object(也就是接口函數中的返回 Call 的模版參數的具體類型對象)
  4. 利用回調將第三步得到的對象,將對象傳回給 UI 線程,更新 UI。

這里面第三部與第四步是可以合在一起的,但是目前 Retrofit 提供的默認代碼中,會通過 Call,加入 Callback,用戶可以在 Callback 中處理結果。

注解(Annotation)是 Retrofit 預先定義的注解,包括 Http 的各個部分,比如 POST、GET、Query、QueryMap、Field 等等。

3. 流程圖

其中生成 Call 的部分可以看下面關于這個適配器的類圖。

4. 詳細設計

4.1 類圖

首先是整個項目的類圖

對于 Retrofit 項目中 CallAdapter 用著適配器模式也挺巧的,通過適配器將 Callback 回調接口運行在 UI 線程。下面時有關 CallAdapter,Call,Callback 的類圖,其中也是連續用了兩次代理模式。

ExecutorCallback 代理的是用戶自定義的 Callback。通過這種方式讓 OkHttpCall 去執行 Call,讓 ExecutorCallback 將用戶自定義的 Callback 運行在指定線程上。

4.2 類功能詳細介紹

在 Retrofit 開源庫中,Retrofit 類是用戶最基礎的訪問入口。然后 Converter 部分是由用戶自己擴展的,而 Paraser 部分的相關類 RequestBuilder,RequestFactory 等則主要是負責解析接口并且生成 Request,而 Call,CallAdapter 等主要是負責底層的 Http 請求,以及請求后線程轉換。

4.2.1 Retrofit

Retrofit 類是包含了一個構造器 Retrofit.Builder,由 Builder 指定 Retrofit 的相關參數,創建一個新的 Retrofit。Retrofit 中包含了很多重要的成員變量,而這些成員變量都是可以自設置的。

Retrofit 包含以下成員變量:

  • baseUrl: Http 請求的基礎 url,類型是 BaseUrl,包含了 url 函數返回 HttpUrl(OkHttp 的類),由 Retrofit.Builder.baseUrl 設置。
  • client:OkHttp 庫的 OkHttpClient 類型。由 Builder 的 client 函數設置,默認為OkHttpClient()
  • methodHandlerCache:Map 類型,MethodHandler 的緩存,從接口中解析出來,放在這個 map 里面。
  • converterFactories:List 類型,包含了很多 converter 的創建工廠,用戶可以通過 Builder 的 addConverterFactory 來添加。默認添加了 BuiltInConverters。
  • callbackExecutor:回調函數的執行器,也就是回調函數執行的線程,Android 中默認為 MainThreadExecutor。
  • adapterFactories:List 類型,包含了 CallAdapter.Factory,用戶可以通過 Builder 的 addCallAdapterFactory 來添加。Android 中默認添加了 ExecutorCallAdapterFactory。使用 callbackExecutor 作為 Executor。
  • validateEagerly:這個是設置的在創建動態代理對象之前,是否提前解析接口 Method,創建 MethodHandler 并添加到 Cache 中。

Retrofit 重要方法:

  • create(final Class service):T 這個是一個 public 模版方法,用戶可以通過這個方法,傳入接口 Class(T),獲得接口 Class Http 請求的動態代理對象。這是該開源庫的主入口,這個函數先驗證接口以及其方法,然后創建一個匿名 InvocationHandler,在匿名 InvocationHandler 的 invoke 中首先去掉 Object 以及 Platform 默認的方法,然后調用 loadMethodHandler 解析對應的方法(接口方法),創建 MethodHandler 加入到 methodHandlerCache 中,得到 MethodHandler,最后調用 MethodHandler 的 invoke 方法得到返回結果(接口方法的返回類型)。動態代理請見Java 動態代理
  • loadMethodHandler(Method method):MethodHandler<?> 解析對應的方法(接口方法),創建 MethodHandler 加入到 methodHandlerCache 中,返回得到 MethodHandler。
  • nextCallAdapter(CallAdapter.Factory skipPast, Type returnType, Annotation[] annotations):CallAdapter<?> 該方法主要是從 callAdapterFactories 中獲取新的 CallAdapter,它會跳過 skipPast,以及 skipPast 之前的 Factory,然后找到與 returnType 和 annotations 都匹配的 CallAdapterFactory。如果不匹配 CallAdapterFactory 的 get 會返回 null,所以搜索 Factories 的時候,直到搜索到返回非 null 就找到對應的了。

如果沒有找到對應的 CallAdapterFactories,得到 CallAdapter,則該方法會拋出一個 IllegalArgumentException 異常,異常里面的 message 會是"Could not locate call adapter for ",如果遇到這個異常,則去判斷對應的方法的返回類型是不是與 CallAdapterFactory 不匹配。

  • requestConverter(Type type, Annotation[] annotations):Converter<T, RequestBody> 也是模版方法,該方法返回 Converter。利用 converterFactories 創建一個與 RequestBody 對應的 Converter 對象。 如果沒有找到對應的 ConverterFactory,得到 Converter,則該方法會拋出一個 IllegalArgumentException 異常,異常里面的 message 會是"Could not locate RequestBody converter for "。同樣,如果遇到這個異常,則去判斷對應的方法的返回類型是不是與 ConverterFactory 不匹配。
  • responseConverter(Type type, Annotation[] annotations): Converter<ResponseBody, T> 與 requestConverter 類似,不過該方法對應的是 Response。
4.2.2 MethodHandler

MethodHandler 是 retrofit 中連接了解析部分,執行部分,轉換部分的一個關鍵的中間類。不過 MethodHandler 的代碼量很少。它可以說是連接各個部分的橋梁,也是接口方法的描述類。它有包含了 retrofit,requestFactory,callAdapter,responseConverter 成員變量。主要方法如下

  • create(Retrofit retrofit, Method method):MethodHandler<?> 這是個靜態方法。MethodHandler 的創建方法,在這個方法里面通過創建 CallAdapter,responseConverter,requestFactory,最后創建 MethodHandler。
  • createCallAdapter(Method method, Retrofit retrofit): CallAdapter<?> 這是個靜態方法。通過 retrofit 的 newCallAdapter 創建 CallAdapter
  • createResponseConverter(Method method,Retrofit retrofit, Type responseType):Converter<ResponseBody, ?> 這是個靜態方法。通過 retrofit 的 responseConverter 方法得到 responseConverter
  • invoke(Object... args):Object 通過 callAdapter 的 adapter 將 OkHttpCall 轉換成需要返回的 Call
4.2.3 Converter 與 Converter.Factory

這兩個類別都是在 Converter 文件下。Converter 是接口,Factory 抽象類,很簡短。

Factory 主要是負責生成兩種 Converter。Retrofit 實現了一個簡單的 BuiltInConverters。

4.2.4 Call

這是 Retrofit 的框架基礎接口。它是 Retrofit 的發送請求給服務器并且返回響應體的調用。每個 Call 都有自己的 HTTP 請求和相匹配的響應。 它有如下四個接口:

  • execute 同步執行請求 Response<T> execute() throws IOException;
  • enquene 異步執行請求,并且使用 Callback 作為請求結束后的回調。 void enqueue(Callback<T> callback);
  • cancel 取消請求 void cancel();
  • clone 復制請求,如果需要很多相同的 Call,可以通過 clone 復制。 Call<T> clone();
4.2.5 CallAdapter

這是 Retrofit 的框架基礎接口。CallAdapter 是將一個 Call 適配給另外一個 Call 的適配器接口。它有以下兩個接口:

  • responseType 返回請求后,轉化的參數 Type 類型。 Type responseType();
  • adapt 適配,將一個 Call 轉換成另外一個 Call。 <R> T adapt(Call<R> call);
4.2.6 Callback

請求結構的回調接口。在 Call 的 enquene 接口中使用 有如下兩個方法

  • onResponse 返回響應體 void onResponse(Response<T> response, Retrofit retrofit);
  • onFailure 請求失敗的時候,比如網絡或者一些難以預料的異常。 void onFailure(Throwable t);
4.2.7 OkHttpCall

實現了 Call 接口,但同樣是模版類。首先介紹一下 OkHttpCall 的主要函數:

  • createRawCall

private com.squareup.okhttp.Call createRawCall() 根據由 requestFactory 根據 args 創建一個 Request,然后利用這個 Request 創建一個 okhttp.Call。

  • parseResponse private Response<T> parseResponse(com.squareup.okhttp.Response rawResponse) throws IOException

解析 okhttp.Response,

  1. 首先將 body 讀取出來作為 rawBody,然后用 OkHttpCall.NoContentResponseBody 作為新的 Body,創建新的 rawResponse。
  2. 判斷 Response.code(),如果不在 200 范圍內,讀取 rawBody 出來,返回一個錯誤的 retrofit 的 Response。如果 code 為 204 或 205(沒有返回內容),則返回一個 body 為空的 retrofit 的 Response。
  3. 如果 code 正常,則用 OkHttpCall.ExceptionCatchingRequestBody 包裝一下 rawBody, 然后使用 responseConverter 將包裝后的 catchingBody 轉化為具體的返回類型數據。

OkHttpCall 是將 Request 放入到 okhttp 的 Call 里面執行,執行完成后,又將 okhttp 的 Call 返回的 Response 轉化為 retrofit 的 Response,在此同時將 Body 里面的內容,通過 converter 轉化為對應的對象。這個 OkHttpCall

4.2.8 Response

這個類是包含了具體返回對象的響應體。里面包含了模版參數 T 類型的 body 對象,以及 okhttp 的 Response。

4.2.9 注解類

在 Retrofit 里面創建了 Body 注解,Filed 注解(Field,FieldMap),請求方法注解(DELETE,GET,PATCH,POST,PUT),請求頭注解(HEAD,Header,Headers),multipart 注解(Part,Multipart,PartMap),接口加碼(FormUrlEncoded),Url,Streaming,查詢(Query,QueryMap),參數路徑(Path),HTTP

4.2.10 RequestBuilderAction

這是一個抽象類,只有一個未實現的 perform 方法。

abstract void perform(RequestBuilder builder, Object value);

但是在 RequestBuilderAction 類里面有很多 RequestBuilderAction 的子類,分別對應注解類。Url,Header,Path,Query,QueryMap,Field,FieldMap,Part,PartMap,Body 都是在 RequestBuilderAction 的內部類,并且繼承了 RequestBuilderAction。RequestBuilder 就是將對應注解的值給 RequestBuilder。

4.2.11 RequestBuilder

這是一個 okhttp.Request 的創建類。負責設置 HTTP 請求的相關信息,創建 Request。它主要有以下方法:

  • RequestBuilder
  • setRelativeUrl
  • addHeader
  • addPathParam
  • canonicalize static 方法
  • canonicalize
  • addQueryParam
  • addFormField
  • addPart
  • setBody
  • build

它的構造方法如下: RequestBuilder(String method, HttpUrl baseUrl, String relativeUrl, Headers headers, MediaType contentType, boolean hasBody, boolean isFormEncoded, boolean isMultipart)

RequestBuilder 就是創建請求。

4.2.12 RequestFactory

RequestFactory 是創建 Request,他有個 create 方法,

Request create(Object... args) {

參數是接口函數對應的參數值,cerate 是創建 RequestBuilder,遍歷 RequestFactory 的成員變量 requestBuilderActions,設置好 RequestBuilder,最后創建 Request 返回。

4.2.13 RequestFactoryParser

這個類主要是接口函數 Method 的每個注解。入口函數是 parse。

先解析方法注解(應用到方法上的注解),比如說 FormUrlEncoded,Headers。得到對應的值。

然后再解析方法參數注解(應用到方法參數上的注解),在解析方法參數注解的時候,會生成一個 requestBuilderActions 數組,對應到每個參數。每個 Action 都對應了每個函數參數的處理。等到具體函數調用的時候,跟函數具體的參數值對應。也就是 RequestFactory 與 Builder 的工作了,這部分是等到運行的時候才能夠確定的。

4.2.14 BuiltInConverters,OkHttpResponseBodyConverter,VoidConverter,OkHttpRequestBodyConverter

BuiltInConverters 繼承自 Converter.Factory,返回的 responseConverter 是 OkHttpResponseBodyConverter 或 VoidConverter,也就是接口方法返回的職能是 OkHttp 的 ResponseBody,或者 Void。 返回的 requestConverter 是 OkHttpRequestBodyConverter,接口方法的參數中如果使用 Body,那 Body 也只能是 OkHttp 的 RequestBody。

VoidConverter: 將 OkHttp 的 ResponseBody 轉化為 Void。 OkHttpResponseBodyConverter:將 OkHttp 的 ResponseBody 轉化為 OkHttp 的 ResponseBody。如果是 Streaming 標記的接口的話,利用 Utils.readBodyToBytesIfNecessary 緩沖整個 body。 OkHttpRequestBodyConverter:將 OkHttp 的 RequestBody 轉化為 OkHttp 的 RequestBody。

4.2.15 PlatForm.Android.MainThreadExecutor

一個 Executor,通過 android Handler 將 Runnable 執行在 UI 線程中。

4.2.16 Utils

這是 Retrofit 中的一個工具類,里面包含了很多范型的檢查、操作。另外以及一些基本的工具性的功能。下面是它里面的函數:

  • checkNotNull <T> T checkNotNull(T object, String message) 檢查非空,如果是 null,則拋出 NullPointerException,內容提示為 message。

  • closeQuietly static void closeQuietly(Closeable closeable) 靜默地關閉 Closeable 對象。不會拋出異常

  • isAnnotationPresent static boolean isAnnotationPresent(Annotation[] annotations,Class<? extends Annotation> cls)判斷 cls 是否是 annotations 里面的一個實例。如果在則返回 true。

  • readBodyToBytesIfNecessary static ResponseBody readBodyToBytesIfNecessary(final ResponseBody body) throws IOException如果 body 非 null 的話,把整個 body 讀取出來(讀取到 buffer),返回再返回一個 ResponseBody。

  • validateServiceInterface static <T> void validateServiceInterface(Class<T> service) 驗證接口是否有效,這個接口就是用戶自定義的接口。如果不是接口,或者里面沒有任何函數,則拋出 IllegalArgumentException 異常。

  • getSingleParameterUpperBound public static Type getSingleParameterUpperBound(ParameterizedType type) 該函數獲取 type 的單個模版參數的上屆。如果 type 有多個類型,函數會拋出異常,如果模版參數不是 WildcardType,則直接返回模版參數類型

  • hasUnresolvableType public static boolean hasUnresolvableType(Type type) 判斷是否有不能分解的類型,比如有 TypeVariable,WildcardType 等

  • getRawType public static Class<?> getRawType(Type type)這個方法是從 Gson 里面截取的,獲取 type 的實際類型。

  • methodError static RuntimeException methodError(Method method, String message, Object... args) static RuntimeException methodError(Throwable cause, Method method, String message,Object... args) 兩個重載函數,拋出方法錯誤異常

  • getCallResponseType static Type getCallResponseType(Type returnType)獲取返回 Call 的返回類型,必須是模版參數類型,并且 Call 的模版參數不能是 retrofit.Response.class。返回 getSingleParameterUpperBound(returnType)

4.3 擴展

Retrofit 是很適合擴展的,里面設計的 Call,以及 Converter 就是為了方便擴展使用。

4.3.1 Converter

Retrofit 提供的默認的 Converter 只會返回 ResponseBody,如果我們想要返回具體的 Object,我們可以使用另外的第三方包,并且在創建 Retrofit 的時候添加對應的 ConverterFactory。這里有 6 個序列化第三方庫:

  • Gson: com.squareup.retrofit:converter-gson
  • Jackson: com.squareup.retrofit:converter-jackson
  • Moshi: com.squareup.retrofit:converter-moshi
  • Protobuf: com.squareup.retrofit:converter-protobuf
  • Wire: com.squareup.retrofit:converter-wire
  • Simple XML: com.squareup.retrofit:converter-simplexml
4.3.2 Rxjava

retrofit 也可以與Rxjava聯合起來使用,之前的版本使用范例可以參考http://randomdotnext.com/retrofit-rxjava/

  • adapter-Rxjava: com.squareup.retrofit:adapter-rxjava

正在開發中,主要是通過擴展 CallAdapter,將之前 Call,轉換為 rxjava 需要的 Observable<?>。

5 說明

這篇文章不是我寫的,本文補全了好多我之前對Retrofit的遺漏知識,所以復制粘貼下來當作筆記,如果其他同學看到,還請多看原作者的原文章:https://github.com/android-cn/android-open-project-analysis/tree/master/tool-lib/network/retrofit

?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,428評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,024評論 3 413
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,285評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,548評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,328評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,878評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,971評論 3 439
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,098評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,616評論 1 331
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,554評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,725評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,243評論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 43,971評論 3 345
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,361評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,613評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,339評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,695評論 2 370

推薦閱讀更多精彩內容