Retrofit 原始碼剖析-深入

wzgiceman發表於2017-01-19

背景

前一章節,先系統的講解了關於Retrofit實現當中的主要技術動態代理,本篇詳細結合動態代理在Retrofit中的應用,擴充套件到結合RxJava來使用

Retrofit原始碼解析-動態代理

思路

要深入研究一款開源專案最好的入口就是它所暴露出來的外部使用介面,按照這個思路,所以需要大體先了解Retrofit的基本使用,這裡就不闡述這些基礎的知識,可以檢視以前的部落格

RxRetrofit-專欄

專案分析

想要弄清楚 Retrofit 的細節,先來看一下 Retrofit 原始碼的組成:

這裡寫圖片描述

  • 一個 retrofit2.http 包,裡面全部是定義 HTTP 請求的註解,比如 GET、POST、PUT、DELETE、Headers、Path、Query 等等

  • 餘下的 retrofit 2.0 包中十幾個類和介面就是全部 retrofit 的程式碼了,程式碼真的很少,很簡單,因為retrofit把網路請求這部分功能全部交給了 okHttp 了

初始物件

先上一段使用Retrofit開始前的初始程式碼

      //手動建立一個OkHttpClient並設定超時時間快取等設定
        OkHttpClient.Builder builder = new OkHttpClient.Builder();
        builder.connectTimeout(basePar.getConnectionTime(), TimeUnit.SECONDS);

        /*建立retrofit物件*/
        Retrofit retrofit = new Retrofit.Builder()
                .client(builder.build())
                .addConverterFactory(ScalarsConverterFactory.create())
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                .baseUrl(basePar.getBaseUrl())
                .build();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

這裡的入口Builder()方法進入原始碼

   public static final class Builder {
    private Platform platform;
    private okhttp3.Call.Factory callFactory;
    private HttpUrl baseUrl;
    private List<Converter.Factory> converterFactories = new ArrayList<>();
    private List<CallAdapter.Factory> adapterFactories = new ArrayList<>();
    private Executor callbackExecutor;
    private boolean validateEagerly;

    Builder(Platform platform) {
      this.platform = platform;
      // 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());
    }

    public Builder() {
      this(Platform.get());
    }
xxxx
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

Builder類中主要是記錄請求過程中的一些配置資訊,比如基礎url,當然重要的是addConverterFactory方法記錄Converter資料轉換器;addCallAdapterFactory方法記錄CallAdapter-HTTP請求返回資料的型別.

Converter資料轉換器

Converter資料轉換器預設存放在retrofit-converters中 
這裡寫圖片描述

Converter採用介面抽象的方式,靈活了轉換器的種類,常用的如上圖中的GsonString,同樣這裡可以可以自由擴充套件,實現Converter.Factory介面

CallAdapter請求返回資料的型別

工程位置如圖

這裡寫圖片描述

這裡同Converter資料轉換器思想一樣同樣是採用抽象介面的方式,繼承CallAdapter.Factory類實現擴充套件,正如上圖中箭頭指示的顯示擴充套件的RxJava2的擴充套件

資訊的轉換

Builder記錄完資料後通過build()方法,將收集的資訊傳遞給最後的Retrofit物件中,並且初始了okhttp3.Call.Factory請求處理類,從中可以看出Retrofit2.0底層的http請求預設就是通過okhttp3處理的。

  /**
     * Create the {@link Retrofit} instance using the configured values.
     * <p>
     * Note: If neither {@link #client} nor {@link #callFactory} is called a default {@link
     * OkHttpClient} will be created and used.
     */
    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> adapterFactories = new ArrayList<>(this.adapterFactories);
      adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));

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

      return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
          callbackExecutor, validateEagerly);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

動態代理

Retrofit初始完成以後,需要呼叫create方法傳入一個Java介面抽象類物件作為引數。其實這一步真是Retrofit動態代理生成的地方

原始碼:

  @SuppressWarnings("unchecked") // Single-interface proxy creation guarded by parameter safety.
  public <T> T create(final Class<T> service) {
    Utils.validateServiceInterface(service);
    if (validateEagerly) {
      eagerlyValidateMethods(service);
    }
    return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
        new InvocationHandler() {
          private final Platform platform = Platform.get();

          @Override public Object invoke(Object proxy, Method method, Object... args)
              throws Throwable {
            // If the method is a method from Object then defer to normal invocation.
            if (method.getDeclaringClass() == Object.class) {
              return method.invoke(this, args);
            }
            if (platform.isDefaultMethod(method)) {
              return platform.invokeDefaultMethod(method, service, proxy, args);
            }
            ServiceMethod serviceMethod = loadServiceMethod(method);
            OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
            return serviceMethod.callAdapter.adapt(okHttpCall);
          }
        });
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

使用程式碼:

        HttpTestService httpService = retrofit.create(HttpTestService.class);
        httpService.getAllVedioBy(isAll());
  • 1
  • 2
  • 1
  • 2

上面HttpTestService物件其實是一個動態代理物件,並不是一個真正的 HttpTestService介面的implements物件,當 httpService物件呼叫 getAllVedioBy方法時,執行的是下面動態代理方法。

 ServiceMethod serviceMethod = loadServiceMethod(method);
            OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
            return serviceMethod.callAdapter.adapt(okHttpCall);
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

其中主要ServiceMethod是主要的處理物件

ServiceMethod資訊類

當呼叫loadServiceMethod方法,將抽象HttpTestService中通過註解定義的資訊收集起來存放於ServiceMethod中,其中主要包含了一下資料:

  • OkHttpClient:傳送網路請求的工具

  • RequestFactory: 類似於 Volley 中的 Request,包含了HTTP請求的Url、Header資訊,MediaType、Method以及RequestAction陣列

  • CallAdapter:HTTP請求返回資料的型別

  • Converter:資料轉換器

最後Map<Method, ServiceMethod> serviceMethodCache儲存全部的 ServiceMethod資訊類

  ServiceMethod<?, ?> loadServiceMethod(Method method) {
    ServiceMethod<?, ?> result = serviceMethodCache.get(method);
    if (result != null) return result;

    synchronized (serviceMethodCache) {
      result = serviceMethodCache.get(method);
      if (result == null) {
        result = new ServiceMethod.Builder<>(this, method).build();
        serviceMethodCache.put(method, result);
      }
    }
    return result;
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

debug可檢視當前ServiceMethod物件收集的資訊

這裡寫圖片描述

收集完資訊以後執行了

 OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
  • 1
  • 1

這一步對okhttp熟悉的同學肯定知道了,就是通過okhttp請求資料,其中serviceMethod是請求的地址資訊,args是當前介面需要的引數資訊。

執行完http請求以後,執行了

 serviceMethod.callAdapter.adapt(okHttpCall);
  • 1
  • 1

這一步是將okhttp獲取到的資料傳遞給一開始Retrofit通過builder指定的RxJavaCallAdapterFactory類,那它是如何一步步處理的呢?

其實在一開始的獲取serviceMethod方法loadServiceMethod(Method method)中,已經將Retrofit物件傳遞給了serviceMethod物件

核心程式碼:

//這裡的this即是`Retrofit`物件
result = new ServiceMethod.Builder(this, method).build();
  • 1
  • 2
  • 1
  • 2

通過.Builder(this, method).build()方法將Retrofit一開始的builder資訊全部傳遞給了現在的serviceMethod物件,所以當執行

 serviceMethod.callAdapter.adapt(okHttpCall);
  • 1
  • 1

serviceMethod.callAdapter便制動轉換成了RxJavaCallAdapterFactory,呼叫CallAdapter抽象介面adapt進入到RxJavaCallAdapterFactory類中

原始碼:

   @Override public <R> Observable<Response<R>> adapt(Call<R> call) {
      Observable<Response<R>> observable = Observable.create(new CallOnSubscribe<>(call));
      if (scheduler != null) {
        return observable.subscribeOn(scheduler);
      }
      return observable;
    }
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

最後生成RxJavaObservable物件,後面通過得到的Observable物件就是對資料的處理了。

到此基於Retrofit結合RxJava原始碼的解讀就完成了,回頭看整個過程其實還是很簡單,作者條理清晰理解起來也很簡單,一口氣終於把Retrofit大體擼了一遍。

總結

Retrofit 非常巧妙的用註解來描述一個 HTTP 請求,將一個 HTTP 請求抽象成一個 Java 介面,然後用了 Java 動態代理的方式,動態的將這個介面的註解“翻譯”成一個 HTTP 請求,最後再執行這個 HTTP 請求

Retrofit的功能非常多的依賴 Java 反射,程式碼中其實還有很多細節,比如異常的捕獲、丟擲和處理,大量的 Factory 設計模式。

Retrofit 中介面設計的恰到好處,在你建立 Retrofit 物件時,讓你有更多更靈活的方式去處理你的需求,比如使用不同的 Converter、使用不同的 CallAdapter,這也就提供了你使用 RxJava 來呼叫 Retrofit 的可能。

專欄

RxJava+Retrofit+OkHttp深入淺出-終極封裝專欄)

原始碼

retrofit2.0

建議

如果你有任何的問題和建議歡迎加入QQ群告訴我!

相關文章