Android Retrofit 閱讀筆記

綜述

Retrofit 並不能說是一個網絡請求庫,它只是一個網絡請求庫的封裝庫,在其內部網絡請求是直接交給 OkHttp 完成的,與 Volley 相比,OkHttp 缺乏了一些更人性化的功能,它更多地專一於如何把一個網絡請求作好,因此在使用 OkHttp 進行請求的時候,咱們要本身生成一個 Request,再自行處理 Response 成咱們須要的數據格式。好像是爲了彌補這一缺點,Square 又基於 OkHttp 給出了一個封裝庫,Retrofit ,Retrofit 的出現,直接將 OkHttp 的使用體驗提高了幾個檔次,使用 Retrofit 進行網絡請求,咱們將不會再看到 Request 、Response ,咱們聲明一個方法,就表明了一個 Request ,而這個方法的返回數據,就是咱們想要的 Response 。 這種方式改變了原始意義上的網絡請求,用戶再也不關心什麼 Request 和 Response ,而是這個網絡請求須要什麼參數,又會給出什麼結果。 爲了實現以調用方法替代構建 Request 的功能,Retrofit 大量使用了註解。好比,一個網絡請求方法能夠聲明以下:設計模式

interfaceAPI{
 @GET("demo/")
 Call<String>getDemo(@Query("id") id);
 }複製代碼

如上所示,表明一個 GET 請求,請求地址爲 demo/,請求參數爲id=?。而在這個例子中,使用到了兩個註解,分別是 GET 和 Query ,分別表明 GET 方法和 Query 字段。Retrofit 提供的註解類型還遠不止如此,足夠支持絕大部分的網絡請求。數組

流程

建立 Retrofit

Retrofit 類是使用的入口,其create()方法用於建立一個實例,而構建 Retrofit 實例使用了 Build 模式,Build 給 Retrofit 的實例化提供參數。 構造方法參數:安全

  1. okhttp3.Call.Factory,okhttp 的類,默認是 OkhttpClient ,只有一個方法,newCall()bash

  2. HttpUrl,okhttp 中的類,表示一個 Urlmarkdown

  3. List<Converters.Factory>,Converter.Factory 集,用戶自定義若干 + 內建的 BuiltInConverters 。Converter 基本功能,類型轉換器,Factory 提供有三種轉換方式,? -> RequestBody ResponseBody -> ? ? -> String,從轉換雙方類型即可以看出,第一種,將任意一種類型轉換爲 RequestBody ,是爲在發起請求的時候,將用戶所給的參數類型轉換爲標準的請求格式,第二種就是將網絡請求以後獲得的標準的返回格式轉換爲其餘任意類型,第三種,講任意類型轉換爲 String,也用於在網絡請求的時候轉換。網絡

  4. List<CallAdapter.Factory>,CallAdapter.Factory 集,用戶自定義若干 + 默認的,CallAdapter ,Call 適配器,可用於將 Call 類型轉換爲其餘任意類型,API 方法默認返回是 Call<ResponseBody> 類型,經過 Converter 能夠將其轉換爲 Call<String> Call<JsonObject> 格式,可是不能改變 Call 自己,CallAdapter 就是用於解決這個問題,它能夠將 Call<ResponseBody> 轉換爲 Future<String> 格式。因此,在 Converter 的 ResponseBody -> ? 這一階段,徹底是能夠經過 CallAdapter 直接實現的。app

  5. Executor,回調處理器,異步

  6. boolean validateEagerly,是否在建立請求類實例時就加載全部的方法,默認狀況是第一次調用時纔會加載ide

加載 API 接口

建立自定義的代理類,經過重寫方法調用的邏輯,一方面將原生的方法依舊調用,一方面將對應 API 的方法解析成網絡請求的格式。 先將 API 方法解析成 ServiceMethod ,而後再交給 OkHttpCall 處理。ui

ServiceMethod

運行時經過反射解析方法,並將已經解析過的方法加入 serviceMethodCache ,從中取 ServiceMethod 的時候使用了雙重檢測保證線程安全。 不然就調用 ServiceMethod 的 Build 構建出一個實例。Build 完成了解析 Method 的工做,須要先獲得一些 Method 相關的信息,主要的信息有:

  1. Method 的返回類型

  2. Method 的全部註解

  3. Method 的全部參數,包括其數據類型和全部的所有註解 一個方法的默認返回類型應該是 Call<ResponseBody> 類型,因此若是用戶不使用自定義的 CallAdapter 和 Converter ,就只能聲明返回類型爲這個的方法,經過自定義 Converter ,能夠將 Call<ResponseBody> 轉換爲 Call<String>,即將 ResponseBody 轉換爲 Converter ,而使用自定義 CallAdapter ,則能夠直接將 Call<ResponseBody> 轉換爲 Future<String> 。 因此,Method 返回類型的主要做用就是用於查找對應的 CallAdapter ,Retrofit 經過工廠類生成 CallAdapter ,因此須要像 Retrofit 中加入自定義的工廠類,工廠類使用public abstract @Nullable CallAdapter<?, ?> get(Type returnType, Annotation[] annotations,Retrofit retrofit);方法生成 CallAdapter ,Factory 經過這個方法提供的三個參數選擇一個合適的 CallAdapter 返回,如 returnType 是 Future<String> ,則須要生成一個將 Call<?> 轉換爲 Future<String> 的 CallAdapter 。 再說 CallAdapter ,它的主要方法是T adapt(Call<R> call),其中 Call<R> 是原始的類型,T 就是輸出的類型,以上面爲例,R 就是 String ,T 是 Future<String> 。但上面說到過,方法的默認返回類型是 Call<ResponseBody> ,那爲什麼此處輸入類型倒是 Call<R> ?爲何 ResponseBody 會變成一個泛型,這裏則是因爲 Converter 存在的緣由。 上面說到過,Converter 能夠將 Call<ResponseBody> 轉換成 Call<String> ,那麼天然也能夠將其轉換成任意類型,即 Call<R>,因此在 ServiceMethod.Build 的build()方法中,前後建立了 CallAdapter 和 Converter ,而此處也說明了一個問題,那就是 Converter 會先於 CallAdapter 執行,CallAdapter 的執行依賴於 Converter 的執行,或者說,CallAdapter 的輸入就是 Converter 的輸出。 Converter 在兩個位置執行,一是enqueue()方法,一是excute()方法,都是在獲得原始的網絡請求結果以後馬上調用的,而 CallAdapter 的adapt()方法倒是在生成調用方法以後生成 Call 的時候調用,如此看來應該是 CallAdapter 先於 Converter 工做的。從這個邏輯上講確實,可是此刻 CallAdapter 中的 Call 卻並無獲得數據,CallAdapter 最終的目的只是轉換 Call ,以下一個轉換爲 Future 的例子:

@OverridepublicCompletableFuture<R>adapt(finalCall<R>call) {
  finalCompletableFuture<R>future=newCompletableFuture<R>() {
    @Overridepublicbooleancancel(booleanmayInterruptIfRunning) {
      if(mayInterruptIfRunning) {
        call.cancel();
     }
      returnsuper.cancel(mayInterruptIfRunning);
   }
 };
  call.enqueue(newCallback<R>() {
    @OverridepublicvoidonResponse(Call<R>call, Response<R>response) {
      if(response.isSuccessful()) {
        future.complete(response.body());
     } else{
        future.completeExceptionally(newHttpException(response));
     }
   }
    @OverridepublicvoidonFailure(Call<R>call, Throwablet) {
      future.completeExceptionally(t);
   }
 });
  returnfuture;
 }複製代碼

Future 仍是在 Call 獲取數據以後再對結果進行轉遞,而在調用future.complete(response.body())之時,Converter 的convert()方法已經執行了,因此這裏的 R ,就是將 ResponseBody 轉換以後的結果,從這個角度而言,依舊是 Converter 先執行。

因此,CallAdapter 的數據依賴於 Converter 執行的結果,那麼爲了保證 CallAdapter 的輸入類型與 Converter 的輸出一致,CallAdapter 提供了另外一個抽象方法,Type responseType(),這裏返回了一個類型,能夠將其理解爲 CallAdapter 須要的輸入數據類型,而以後建立 Converter 實例的時候,就是根據這個類型查找 Converter.Factory ,繼而生成 Converter ,兩者之間的一致性就能夠由此保證。以上是解析 Method 的第一步,接着會解析 Method 的註解,通常就是聲明瞭請求方法,GET、POST 等,調用的是parseMethodAnnotation()方法。再接着就是解析 Method 的全部參數,根據參數的數據類型和註解生成 ParameterHandler 實例,調用parseParameter()方法。解析的邏輯是比較有意思的,對於一個參數,先判斷註解類型,好比是 Quey 、Filed、Path 或 QueryMap 等,不一樣的類型會生成不一樣的 ParameterHandler ,好比 Query ,須要先根據 Query 這個註解對應的參數的類型,查找到一個 Converter ,使得可以將這個參數轉換爲 String 類型,由於 Query 就應該是一個 String ,若是不是,須要有 Converter 將其轉成 String 。完了以後,便會生成一個 ParameterHandler 實例,ParameterHandler 是一個抽象類,它有許多的子類,並且每個子類對應着一種註解類型,如 ParameterHandler.Query 這個子類就是用於處理 Query 這個註解的,它實現的apply()方法,可以將參數轉換成 ResquestBuilder 中的一個 Query ,以下:

@Overridevoidapply(RequestBuilderbuilder, @NullableTvalue) throwsIOException{
  if(value==null) return; // Skip null values.
  StringqueryValue=valueConverter.convert(value);
  if(queryValue==null) return; // Skip converted but null values
  builder.addQueryParam(name, queryValue, encoded);
 }複製代碼

另外,ParameterHandler 還有iterable()array()方法,可用於處理鏈表、數組等數據,

finalParameterHandler<Iterable<T>>iterable() {
  returnnewParameterHandler<Iterable<T>>() {
    @Overridevoidapply(RequestBuilderbuilder, @NullableIterable<T>values)
        throwsIOException{
      if(values==null) return; // Skip null values.
      for(Tvalue: values) {
        ParameterHandler.this.apply(builder, value);
     }
   }
 };
 }複製代碼

其本質是重寫其apply()方法,在這個方法裏面先遍歷 iterable ,再調用實際的apply()方法寫到 RequestBuilder 中。 當全部參數的 ParameterHandler 生成完了以後,ServiceMethod 也就算建立好了。接下來即是建立一個 Call 返回。

Call

接下來將 ServiceMethod 做爲參數構建 OkHttpCall 實例,OkHttpCall 實現了 Call 接口,封裝了 okhttp.Call ,okhttp.Call 的實例會在發起網絡請求的時候,由 ServiceMathod 建立,SeriviceMethod 則利用存儲在其內部的各類參數,如 Url、ParameterHandler 等構建出 RequestBuilder ,再由 OkHttpClient 建立出 okhttp.Call 實例返回。 OkHttpCall 實現的是 Call 接口,調用方法的最後一步,就是將 Call<ResponseBody> 由 CallAdapter 轉換成方法的實際返回類型,Retrofit 有一個默認的 CallAdapter ,它的adapt()方法的實現就是什麼都不作,直接將 Call 返回。這也就解釋了爲何,若是沒有加入自定義 CallAdapter ,方法的返回類型就必須是 Call<?> 。 還有一點,默認的 CallAdapter 並非什麼都沒作,它將 enqueue()的 Callback 的回調切換到了主線程執行,由於在 OkHttp 中調用的enqueue()會將請求放在線程池裏執行。 由此,一個網絡請求就建立完成了,下一步是執行網絡請求。

執行網絡請求

就以默認狀況下返回的 Call<ResponseBody> 爲例,Call 中聲明瞭一些網絡請求的方法,如enqueue()execute(),這些方法會調用 okhttp.Call 的對應方法,好比

call.enqueue(newokhttp3.Callback() {
  @OverridepublicvoidonResponse(okhttp3.Callcall, okhttp3.ResponserawRespon
    Response<T>response;
    try{
      response=parseResponse(rawResponse);
   } catch(Throwablee) {
      callFailure(e);
      return;
   }
    try{
      callback.onResponse(OkHttpCall.this, response);
   } catch(Throwablet) {
      t.printStackTrace();
   }
 }
  @OverridepublicvoidonFailure(okhttp3.Callcall, IOExceptione) {
    callFailure(e);
 }
  privatevoidcallFailure(Throwablee) {
    try{
      callback.onFailure(OkHttpCall.this, e);
   } catch(Throwablet) {
      t.printStackTrace();
   }
 }
 });複製代碼

Call 有兩種網絡請求方式,分別對應同步和異步請求,若是是同步請求,就會直接返回一個 Response<T> ,若是是異步請求,則還須要藉助回調類 Callback 完成。

返回請求結果

Retrofit 將 OkHttp 返回的數據進一步包裝獲得 Response<T> ,包裝使用的方法就是parseResponse(),內部邏輯爲,先獲得 okhttp.Response 的 ResponseBody ,再調用 Converter 將 ResponseBody 轉換爲其它類型。

總結

以上是 Retrofit 一個網絡請求的所有流程。 在代碼裏,Retrofit 使用了許多設計模式,例如適配器模式(CallAdapter,Converter,ParameterHandler.iterable()),工廠模式(CallAdapter.Factory、Converter.Factory),代理模式(解析 Method 的時候,使用Proxy.newProxyInstance()生成了一個代理實例,自定義的方法纔去了自定義的執行模式),Build 模式(Retrofit.Build、ServiceMethod.Build),單例模式(BuiltInConverters.ToStringConverter),策略模式(ParameterHandler.Query…)。 除此,充分利用 Java 註解也是 Retrofit 一大特點,Retrofit 使用了一個 Method 全部能夠利用的地方,方法註解、方法參數、參數註解甚至參數類型、返回類型,都被 Retrofit 加以利用,其餘較多使用註解的庫好比 EventBus、ButterKnife ,也有與之相似的一些效果。 從一個封裝庫的角度來講,Retrofit 是一個比較成功的庫,用戶能夠經過加入自定義的 CallAdapter 和 Converter ,將網絡請求方法的返回類型設置爲任何所需的,而且這些並不須要用戶本身寫,Retrofit 有已經寫好的適配器,將其加入到項目中便可使用。即使用戶不想使用自定義的,光是將方法轉換爲 Request 的功能也值得一用了。 惟一不足的,我認爲是 Retrofit 只能使用 OkHttp 做爲內在的網絡請求庫,Retrofit 的實現也在不少地方依賴於 OkHttp ,雖然 Retrofit 可能就是做爲 OkHttp 的封裝庫而存在的,但我以爲 Retrofit 不該止步於此。

相關文章
相關標籤/搜索