Retrofit原始碼學習筆記

Zwww發表於2019-05-14

最近看了Retrofit(2.5.0)的部分原始碼,藉此部落格記錄下自己對Retrofit的理解,功力善淺,如有錯誤歡迎各位大佬指正。

1、簡單使用Retrofit進行非同步請求

image.png
image.png

Retrofit的請求分為四步

  • 1、建立Retrofit物件,在這裡新增網路請求基地址,新增實際網路請求物件Okhttp,新增資料轉換器工廠,支援Gson,JackSon,Guava,Java8等等,這裡我們使用的是Gson的轉換器工廠。然後再新增CallAdapter工廠,支援Guava,Java8.Rxjava等,這裡我們用的是RxJava2的CallAdapter工廠。
  • 2、我們是先建立一個管理網路請求的介面(RetrofitInterface),然後再這個介面裡去定義網路請求。
  • 3、我們根據RetrofitInterface定義的網路請求介面拿到Retrofit的Call物件。
  • 4、通過Call物件的enqueue方法執行網路請求。

2、Retorfit類分析

採用了構建者模式來構建,避免在構造方法傳入過多引數導致使用者混淆。

先分析Retrofit的內部類Builder

Builder成員變數
  public static final class Builder {
    private final Platform platform;
    private @Nullable okhttp3.Call.Factory callFactory;
    private @Nullable HttpUrl baseUrl;
    private final List<Converter.Factory> converterFactories = new ArrayList<>();
    private final List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>();
    private @Nullable Executor callbackExecutor;
    private boolean validateEagerly;
    ......省略
    }
複製程式碼

這裡只貼出了Builder類的成員變數,可以看出這個類的成員變數不多,也就是說Retrofit的配置並不像okhttp那麼多

  • 1、platform表示的是平臺,因為Retrofit不止支援Android平臺他還支援Java8
  • 2、callFactory是Okhttp3.Call的Factory介面例項,這裡我們建立時傳入的是OkhttpClient物件,預設也是OkhttpClient物件。
  • 3、baseUrl也就是我們的網路請求基地址
  • 4、converterFactories 是一個存放資料轉換器工廠的資料集合
  • 5、callAdapterFactories是一個存放callAdapter工廠的資料集合
  • 6、callbackExecutor預設是MainThreadExecutor這個類例項。裡面有一個主執行緒的handler,所以很明顯這個是做執行緒切換的。
    Retrofit原始碼學習筆記
  • 7、validateEagerly是一個標記位,在後面會被用到。
Builder構造方法
  Builder(Platform platform) {
      this.platform = platform;
    }

    public Builder() {
      this(Platform.get());
    }

    Builder(Retrofit retrofit) {
      platform = Platform.get();
      callFactory = retrofit.callFactory;
      baseUrl = retrofit.baseUrl;

      // Do not add the default BuiltIntConverters and platform-aware converters added by build().
      for (int i = 1,
          size = retrofit.converterFactories.size() - platform.defaultConverterFactoriesSize();
          i < size; i++) {
        converterFactories.add(retrofit.converterFactories.get(i));
      }

      // Do not add the default, platform-aware call adapters added by build().
      for (int i = 0,
          size = retrofit.callAdapterFactories.size() - platform.defaultCallAdapterFactoriesSize();
          i < size; i++) {
        callAdapterFactories.add(retrofit.callAdapterFactories.get(i));
      }

      callbackExecutor = retrofit.callbackExecutor;
      validateEagerly = retrofit.validateEagerly;
    }
複製程式碼
  • 1、預設的構造方法只是呼叫了平臺引數的構造方法,並給platform賦值。所以在Android裝置上platform是Platform的內部類Android類例項。
    image.png
  • 2、 我們也可以通過傳入Retrofit物件來初始化Builder,同樣platform還是根據平臺來賦值。callFactory,baseUrl ,callbackExecutor ,validateEagerly 取的都是傳入Retrofit例項的值。converterFactories和callAdapterFactories只新增通過Builder的addConverterFactory方法和addCallAdapterFactory方法新增的Converter.Factory和CallAdapter.Factory。因為Builder的build方法會新增預設的轉換器和介面卡到converterFactories和callAdapterFactories裡。簡而言之就是隻新增使用者之前構建Retrofit時傳入的Converter.Factory和CallAdapter.Factory。
Builder的build方法
 public Retrofit build() {
      if (baseUrl == null) {
        throw new IllegalStateException("Base URL required.");
      }

      okhttp3.Call.Factory callFactory = this.callFactory;
      if (callFactory == null) {
        callFactory = new OkHttpClient();
      }

      Executor callbackExecutor = this.callbackExecutor;
      if (callbackExecutor == null) {
        callbackExecutor = platform.defaultCallbackExecutor();
      }

      // Make a defensive copy of the adapters and add the default Call adapter.
      List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
      callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor));

      // Make a defensive copy of the converters.
      List<Converter.Factory> converterFactories = new ArrayList<>(
          1 + this.converterFactories.size() + platform.defaultConverterFactoriesSize());

      // Add the built-in converter factory first. This prevents overriding its behavior but also
      // ensures correct behavior when using converters that consume all types.
      converterFactories.add(new BuiltInConverters());
      converterFactories.addAll(this.converterFactories);
      converterFactories.addAll(platform.defaultConverterFactories());

      return new Retrofit(callFactory, baseUrl, unmodifiableList(converterFactories),
          unmodifiableList(callAdapterFactories), callbackExecutor, validateEagerly);
    }
複製程式碼
  • 1、檢測BaseUrl是否為null,檢測callFactory 是否為null,預設為構建的一個OkhttpClient例項。檢測callbackExecutor 是否為null,預設為我們前面提到的MainThreadExecutor例項。然後為callAdapterFactories新增傳入的CallAdapter.Factory例項和平臺預設的CallAdapter.Factory例項。為converterFactories 新增BuiltInConverters例項,傳入的Converter.Factory例項和平臺預設的Converter.Factory例項。
    image.png
    由上面程式碼我們可以看出Android平臺預設的CallAdapter.Factory在7.0以上為CompletableFutureCallAdapterFactory和ExecutorCallAdapterFactory,在7.0以下為ExecutorCallAdapterFactory。
    image.png
    由上面程式碼我們可以看出Android平臺預設的Converter.Factory在7.0以上為OptionalConverterFactory,在7.0以下為空集合。
  • 2、最後將通過這些引數構建Retrofit物件,這時會用呼叫Collections的unmodifiableList方法將converterFactories和callAdapterFactories都轉換為一個只讀的快速訪問的list,UnmodifiableRandomAccessList例項。所以通過Builder構建的Retrofit物件裡面的converterFactories和callAdapterFactories例項都是隻讀的,要是進行寫操作會丟擲UnsupportedOperationException異常。
    image.png

接著分析Retrofit

Retrofit的成員變數

Retrofit的成員變數和Retrofit的內部類Builder裡面的成員變數是基本一致的,這裡就不做闡述了。

Retrofit的create方法

我們前面總結的Retrofit簡單非同步請求的第三步就是通過呼叫Retrofit的creat方法來建立RetrofitInterface介面例項。我們現在來看下內部是如何工作的。

image.png

  • 1、首先檢測傳入的Class是不是介面。
  • 2、然後接下來我們可以看到validateEagerly這個引數的使用了,預設是false。如果我們設定為true的話,會呼叫eagerlyValidateMethods方法。
    image.png
    這裡會遍歷傳入的RetrofitInterface介面裡面的所有方法,如果不是預設方法則繼續走156行,呼叫Retrofit的loadServiceMethod方法。
    image.png
    這裡可以看到原來Retrofit裡面還有一個serviceMethodCache的成員變數,這個成員變數是一個ConcurrentHashMap,這是一個執行緒安全的map集合,看名字以及上面程式碼我們明白了我們這個集合就是用來快取Method對應的Retrofit裡面的ServiceMethod物件。最後跟蹤原始碼發現通過Method構建了一個HttpServiceMethod例項。可以看出HttpServiceMethod的成員變數都是很重要的,這裡就先不闡述了。從這裡我們也可以看出validateEagerly代表的含義是是否提前快取RetrofitInterface中定義的方法到Retrofit中來。
    image.png
  • 3、我們跳回到之前的Retrofit的create方法,Retrofit的133行建立了一個動態代理類。當RetrofitInterface介面中的方法通過代理執行的時候,便會執行這裡面InvocationHandler匿名內部類的invoke方法。接著往下判斷如果方法是來自Object的方法,則正常呼叫。如果是Andriod平臺預設方法,則會丟擲UnsupportedOperationException異常(下圖是Platform的invokeDefaultMethod方法,內部類Android沒有重寫該方法。)
    image.png
  • 4、接下來是這個方法的核心147行,首先呼叫了loadServiceMethod方法,並通過ConcurrentHashMap獲取有沒有此method對應的HttpServiceMethod例項。有則直接返回,沒有則建立對應的HttpServiceMethod,快取後便返回對應的HttpServiceMethod例項。接著再呼叫invoke,這裡我們可以看到主要是構建了一個OkhttpCall物件,然後用我們的calladapter也就是RxJava2CallAdapter的adapt方法去處理。所以這裡我們就可以看出Retrofit主要是對網路請求方法做了下封裝,實際請求和處理還是交給了我們的OkhttpCall和傳入的介面卡和轉換器。
    image.png

3、HttpServiceMethod類

從上面我們知道了Retrofit的Creat方法建立了一個動態代理,當我們呼叫請求介面方法時,代理的invoke方法便會執行,這時候主要是通過HttpServiceMethod來解析請求介面(RetrofitInterface)中定義的網路請求方法。所以,我這裡想多多認識HttpServiceMethod。

基類ServiceMethod

前面我們提到的Retrofit的loadServiceMethod方法中,用serviceMethodCache來快取Method例項和對應的HttpServiceMethod例項,而這個HttpServiceMethod例項是通過ServiceMethod的parseAnnotations方法獲得的。

image.png
parseAnnotations方法首先構建一個RequestFactory例項
image.png
這裡同樣是採用了構建者模式
image.png
獲取了method的註解,形參型別,形參註解。顯而易見,RequestFactory類就是將我們在RetroInteface介面中定義的請求方法註解,返回值,引數型別等等進行解析並儲存。我們接著看ServiceMethod的parseAnnotations方法,接下來是判斷method的返回值是否合法,不能包括可變型別或者萬用字元,並且不能為空,否則丟擲異常,也就是說我們的網路請求方法的返回值不能包括可變型別或者萬用字元,並且不能為空。接著繼續呼叫子類HttpServiceMethod的parseAnnotations方法

HttpServiceMethod的parseAnnotations方法

image.png
這裡34行就是獲取我們新增到Retorfit成員變數callAdapterFactories中的RxJava2CallAdapterFactory例項然後來建立一個RxJava2CallAdapter例項返回。接下來就是處理method的返回值型別,再通過我們新增到Retorfit成員變數converterFactories中的GsonConverterFactory例項來建立一個GsonResponseBodyConverter例項返回。最後再通過RequestFactory例項,okhttp3.Call.Factory例項,RxJava2CallAdapter例項和GsonResponseBodyConverter例項來構建一個HttpServiceMethod例項。 由上面的這些內容我們可以明白HttpServiceMethod這個類的主要功能是通過RxJava2CallAdapter將得到的RequestFactory,okhttp3.Call.Factory,GsonResponseBodyConverter例項建立一個OkHttpCall例項。

4、總結

  • 1、Retrofit內部實際用的網路請求還是OkHttp,我們在介面中定義的請求方法通過反射獲得註解,引數,返回值這些來建立RequestFactory例項,最後再通過RequestFactory例項來建立OkHttp的RealCall物件。
  • 2、Retrofit支援多平臺,資料轉換器和執行排程器,最大程度的滿足不同架構。
  • 3、validateEagerly的值代表是否一開始就直接快取網路請求介面裡面所有的對應的HttpServiceMethod例項。
  • 4、Retrofit主要是採用動態代理的方式在我們執行網路請求方法的時候對其進行解析。
  • 5、Retrofit採用的大量的設計模式(構建者模式,代理模式,簡單工程模式、策略模式,介面卡模式等等)可供我們參考學習。

相關文章