Retrofit 原始碼解析

yangxi_001發表於2016-11-28

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 介面

public interface HttpbinService {
    @GET("/get?arg1=hello")
    Call<HttpbinRequest> testGet();
}

使用方式

Retrofit retrofit = new Retrofit.Builder()
            .baseUrl("https://httpbin.org")
            .addConverterFactory(GsonConverterFactory.create())
            .build();

HttpbinService httpbinService = retrofit.create(HttpbinService.class);

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

Call<HttpbinRequest> call = httpbinService.testGet();

因為介面返回的應該是一個 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。

Retrofit 總體結構

  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. 流程圖

Retrofit 使用流程圖

其中生成 Call 的部分可以看下面關於這個介面卡的類圖。

4. 詳細設計

4.1 類圖

首先是整個專案的類圖 Retrofit UML 圖

對於 Retrofit 專案中 CallAdapter 用著介面卡模式也挺巧的,通過介面卡將 Callback 回撥介面執行在 UI 執行緒。下面時有關 CallAdapter,Call,Callback 的類圖,其中也是連續用了兩次代理模式。

CallAdapter uml 圖

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 也是模版方法,該方法返回 Converter。利用 converterFactories 建立一個與 RequestBody 對應的 Converter 物件。 如果沒有找到對應的 ConverterFactory,得到 Converter,則該方法會丟擲一個 IllegalArgumentException 異常,異常裡面的 message 會是"Could not locate RequestBody converter for "。同樣,如果遇到這個異常,則去判斷對應的方法的返回型別是不是與 ConverterFactory 不匹配。
  • responseConverter(Type type, Annotation[] annotations): Converter 與 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 這是個靜態方法。通過 retrofit 的 responseConverter 方法得到 responseConverter
  • invoke(Object... args):Object 通過 callAdapter 的 adapter 將 OkHttpCall 轉換成需要返回的 Call
  Object invoke(Object... args) {
    return callAdapter.adapt(new OkHttpCall<>(retrofit, requestFactory, responseConverter, args));
  }
4.2.3 Converter 與 Converter.Factory

這兩個類別都是在 Converter 檔案下。Converter 是介面,Factory 抽象類,很簡短。

public interface Converter<F, T> {
  T convert(F value) throws IOException;

  abstract class Factory {
    // 返回將 ResponseBody 轉化為 Type 具體的物件的 Converter
    public Converter<ResponseBody, ?> fromResponseBody(Type type, Annotation[] annotations) {
      return null;
    }

    //返回將函式 Body 引數轉化為 RequestBody 的 Converter
    public Converter<?, RequestBody> toRequestBody(Type type, Annotation[] annotations) {
      return null;
    }
  }
}

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。

  static RequestFactory parse(Method method, Type responseType, Retrofit retrofit) {
    RequestFactoryParser parser = new RequestFactoryParser(method);
    parser.parseMethodAnnotations(responseType);
    parser.parseParameters(retrofit);
    return parser.toRequestFactory(retrofit.baseUrl());
  }

先解析方法註解(應用到方法上的註解),比如說 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 執行緒中。

    static class MainThreadExecutor implements Executor {
      private final Handler handler = new Handler(Looper.getMainLooper());

      @Override public void execute(Runnable r) {
        handler.post(r);
      }
    }
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 整體框架的程式碼並不多,主要是圍繞著 converter,CallAdapter 設計的整個框架。花了兩三天時間耐耐心心地把程式碼也是挺有收穫。Retrofit 用到的基本技術是動態代理,Java 註解,Java 範型。另外如果對設計模式很熟悉的話,讀起來感覺就會事半功倍。整個架構設計的非常好。


轉自:https://github.com/android-cn/android-open-project-analysis/tree/master/tool-lib/network/retrofit

相關文章