Retrofit 的神秘面紗

yiweishi 8年前發布 | 13K 次閱讀 Retrofit Java Android開發 移動開發

初識 Retrofit ,覺得很神奇,體現在以下兩點:

神奇之一:僅憑注解和接口便可執行網絡請求。

public interface GithubService {

    @GET("users/{username}")
    Call<User> getUser(@Path("username") String username);

}

神奇之二:接口方法的返回值變幻無窮。

無論你是用默認的方式,或者是RxJava,無論是在Java或是Android中調用,Retrofit均可滿足,體現在接口的返回值,如下代碼:

public interface GithubService {

    //默認方式
    @GET("users/{username}")
    Call<User> getUserByDefault(@Path("username") String username);

    //RxJava方式
    @GET("users/{username}")
    Observable<User> getUserByRxJava(@Path("username") String username);

}

這兩個神奇之處,猶如兩層神秘面紗,將Retrofit原本俊俏嬌羞的臉龐,遮掩得若隱若現。若不揭開面紗,一睹芳容,便無處釋放程序員們最原始的沖動,于是一起來閱讀源碼吧。

尋找接口的實現類

對于第一個神奇之處,首先我們來看看GithubService接口是如何調用的:

Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com/")
    .build();

GitHubService service = retrofit.create(GitHubService.class);
Call<User> call = githubService.getUser("geniusmart");

仔細斟酌這段代碼, GitHubService 本身是個接口,并調用了 getUser() 方法獲得返回值,那么問題來了:

  1. GitHubService 是由我們定義的接口,但是我們并沒有定義實現類,實現類到底在哪里?
  2. 既然我們沒有定義實現類,那么大膽假設該實現類是由Retrofit框架提供的。對于框架而言,該接口是由開發者自定義的,框架無法做到未卜先知,那么他是如何做到的?

以上兩個問題可以總結為:框架如何動態生成 GitHubService 的實現類? 答案也不難猜測——動態代理模式。(對動態代理有疑問的同學,可以先看下 《公共技術點之 Java 動態代理》

接下來驗證我們的猜測, GitHubService 的來源在此行代碼中:

GitHubService service = retrofit.create(GitHubService.class);

查看 create() 的源碼,所有謎底都將揭開:

通過JDK實現動態代理

這個方法生成并返回了 GitHubService 的動態代理對象,在執行接口的每個方法時,實際執行的是 invoke() ,而該方法里的邏輯便是此框架的主體流程,如下代碼:

public Object invoke(Object proxy, Method method, Object... args){
    //1.負責注解的解析
    ServiceMethod serviceMethod = loadServiceMethod(method);
    //2.負責與OKHttp3的對接,處理同步和異步發送網絡請求
    OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
    //3.第二層面紗,下文再做解釋
    return serviceMethod.callAdapter.adapt(okHttpCall);
}

這個方法里,做了三件事情:

  1. 解析方法的注解,比如 get/post、url、Headers 等,解析的結果存放于 ServiceMethod 對象中。
  2. 創建 OkHttpCall ,該對象負責與OKHttp3對接,處理同步或異步的網絡請求。
  3. 這是第二層神秘面紗的謎底,下文再做解釋。

至此,我們揭開了Retrofit的第一層神秘面紗,這是一位簡單而優雅的小女子,表面樸實無華,恬靜清新,令人迫不及待想要揭開她的第二層面紗,知曉她的內心世界。

變幻無窮的適配

首先來回顧下上文提到的第二個神秘之處,同樣是獲取用戶信息的網絡請求,可以返回 Call<User> 或 Observable<User> ,這是如何做到的?

上文中, invoke() 做的第三件事情便是獲取返回值,如下:

return serviceMethod.callAdapter.adapt(okHttpCall);

其中, adapt 是適配的意思,其目的是變廢為寶,將指定的輸入類型適配成實際需要的輸出類型,來查看下它的源碼:

public interface CallAdapter<T> {
  <R> T adapt(Call<R> call);
}

我們重點關注下 adapt 的輸入輸出。這里的設計非常大膽,輸入是有約束的,即 Call 類型, 而輸出為泛型 T ,也就是說輸出是沒有任何約束的 ,如此一來,擴展性極強,可以指定任何的類型。

剩下來的問題便是:

  1. adapt 由哪個對象觸發,即 CallAdapter 的具體實現是什么?

  2. 輸入類型 Call 的具體實現是什么?

  3. 輸出類型 T 的具體實現是什么?

這里直接給出答案,如下兩圖:

默認方式的適配

RxJava方式的適配

  • 通過上面兩圖,可以看到,輸入對象是固定的,即 OkHttpCall 對象,網絡請求實際上便是由該對象發起的。

  • 默認方式的輸出對象是 ExecutorCallbackCall ,它持有 OkHttpCall 對象,指定了網絡請求執行結束后的回調函數在UI線程中執行。

  • RxJava方式的輸出對象是 Observable ,持有該對象便具備函數式編程的能力。

  • adapt() 的調用者——適配器 CallAdapter 是動態配置的,默認方式無需配置,如果使用RxJava,則需要配置適配器的工廠對象,如下代碼:

    Retrofit retrofit = new Retrofit.Builder()
      .baseUrl(BASE_URL)
      .addConverterFactory(GsonConverterFactory.create())
      .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
      .build();

這樣設計簡直是解耦得一塌糊涂,我們可以任意變換調用方式,也可以任意擴展 CallAdapter 來定義新的調用方式,而框架無需做任何調整。比如 JakeWharton 前幾天開源的RxJava2的適配—— retrofit2-rxjava2-adapter ,比如針對Google函數式編程庫 Agera的適配—— retrofit-agera-call-adapter ,簡直OCP得一塌糊涂。

至此,第二層神秘面紗緩緩而落,這是一位心靈手巧的奇女子,你給她一針一線,她還你一幅刺繡,刺繡上可以是錦繡山河,也可以是紫氣東來;你給她新鮮的食材,她變幻出色香味俱全的美味佳肴。

總結

動態代理、適配是Retrofit的核心,理解清楚這兩點,再去梳理框架的其他細節,方可事半功倍。除此之外,框架內部使用了大量的Builder模式和Factory模式,這兩個模式使得創建對象和獲取對象更有條理,更清晰易懂。值得一提的是,這個框架配備了完整的單元測試用例,非常值得我們學習。

參考文章

http://square.github.io/retrofit/

https://github.com/square/retrofit

 

來自:http://www.jianshu.com/p/0730fc155996

 

 本文由用戶 yiweishi 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!