Retrofit分析-經典設計模式案例

如果你還不知道Retrofit,沒關系,okhttp你總知道吧。retrofit就是對okhttp再做了一層封裝。你只需要通過簡單的配置就能順利使用retrofit來做網絡請求了。還沒有使用過retrofit的小伙伴們,不妨嘗嘗鮮。

本篇是retrofit番外篇。只講retrofit中的設計模式以及我個人的理解與延伸。如果你還沒看過retrofit源碼,不妨先看看這篇 Retrofit分析-漂亮的解耦套路

還是先上圖:

以前用的volley, async-http-lib, xUtils。這些libs基本都是上圖這個workflow。向服務器請求API總共分三步。

  1. build request(API參數配置)
  2. executor(這里可以有很多變體,比如有無隊列,進出順序,線程管理)
  3. parse callback(解析數據,返回T給上層)

如今的retrofit也是換湯不換藥的。也是這三步。

  1. 通過注解配置API參數
  2. CallAdapter (你可以把它理解成executor)
  3. Converter (解析數據并轉換成T)

本來還應該有個 CallFactory 來切換具體的http client的。就像volley那樣>=9使用 HttpUrlConnection , <9使用 HttpClient 。不過我想都是square出品,彼此互推也是理所當然的啊。我相信以后okhttp肯定是唯一的請求client。

上面說 CallAdapter 可以理解成executor,具體是什么,我們下面講具體設計模式時再詳細討論。

好,鋪墊了這么多,我要開車了。Stay先帶你看看沿途一些的設計模式,等到終點了再來看整體的架構。

先來說個最簡單的設計模式。

外觀模式(門面模式)

Retrofit給我們暴露的方法和類不多。核心類就是Retrofit,我們只管配置Retrofit,然后做請求。剩下的事情就跟上層無關了,只需要等待回調。這樣大大降低了系統的耦合度。對于這種寫法,我們叫外觀模式(門面模式)。

幾乎所有優秀的開源library都有一個門面。比如 Glide.with() ImageLoader.load() Alamofire.request() 。有個門面方便記憶,學習成本低,利于推廣品牌。 Retrofit的門面就是 retrofit.create()

當我們自己寫的代碼的時候盡量也要這樣來做。

比如我們有一個獨立并公用的模塊,需要供其他模塊來調用。比如 download , location , socialshare 等

最好我們寫一個 module ,將所有相關的代碼都放在這個 module 中。這是第一步。

第二步,為你的 module 提供一個漂亮的門面。比如下載的 DownloadManager , 經緯度的 LocationTracker , 社交分享的 SocialManager 。它們做為功能模塊的入口,要盡量的簡潔,方法命名好記易理解,類上要有完整的示例注釋

第三步,閉門造車。不管你在里面干什么,外面都是不知道的,就像薛定諤的那只貓,外層不調用它,永遠不知道它是否好用。

不過為了以后好維護,不給他人留坑,還是盡量寫的工整一些。

靜態代理

retrofit里有很明顯的動態代理。不過我們先來說下靜態代理。先普及下英文概念: Proxy->代理。 Delegate->委派,授權。就是把一個功能delegate給proxy去執行。通過接口定義的方法約束,讓外層以為proxy跟delegate是同一個,甚至根本不知道有delegate存在。

拋開理論的描述,我們直接來看下面的代碼。

你可以將 ExecutorCallbackCall 當作是ProxyCall,而真正去執行請求的DelegateCall是 OkHttpCall 。之所以要有個proxy類,是希望在delegate操作的前后去做一些操作。這里的操作就是線程轉換,將子線程切換到主線程上去。

簡單的解釋下,enqueue()方法是異步的,也就是說,當你調用 OkHttpCall 的enqueue方法,回調的callback是在子線程中的,如果你希望在主線程接受回調,那需要通過Handler轉換到主線程上去。 ExecutorCallbackCall 就是用來干這個事。當然以上是原生retrofit使用的切換線程方式。如果你用rxjava,那就不會用到這個 ExecutorCallbackCall 而是 RxJava 的Call了。這里不展開。

動態代理

再來說動態代理。以往的動態代理和靜態代理使用的場景是類似的。都想在delegate調用方法前后做一些操作。如果我的代理類有很多方法,那我得額外寫很多代碼,所以這時候就引入了動態代理。通過動態設置delegate,可以處理不同代理的不同方法。看不懂沒關系,直接上代碼:

簡而言之,動態代理就是攔截調用的那個方法,在方法前后來做一些操作。Retrofit里的動態代理比較巧妙。實際上它根本就沒有delegate。因為這個方法沒有真正的實現。使用動態代理,只是單純的為了拿到這個method上所有的注解。所有的工作都是由proxy做了。比起我們總說 代理就是打log 要高明多了。

我以前自己寫數據庫框架時,也碰到這樣的場景。一個類里有很多一對一,一對多關系。如果從db里fetch出來都去做初始化,那會非常影響性能。但如果不初始化,到使用時再去手動初始化就更麻煩了。怎么辦呢?

class A{
    private B b;
    private ArrayList<C> cs;

    public B getB(){
        return b;
    }

    public ArrayList<C> getCs(){
        return cs;
    }
}

當類A里的get方法被invoke時,我就判斷,這個類有沒有被初始化,如果有,那就不做任何操作。如果沒有,那得等會,我把數據從數據庫中fetch出來給你賦值后,再去invoke。這個場景可以叫懶加載,可以套用AOP面向切面編程。

動態代理能實現這個需求嗎?可以,但是支持的很糟糕。因為動態代理依賴接口實現,總不能將所有的pojo中的方法都申明到接口里吧?那真是要命了。

所以我用了種替代方案,既然是AOP,有個面向切面的框架 AspectJ 。你可以通過它來切入這些get方法,先判斷有沒初始化,然后再返回。

差不多就是這樣,沒有Proxy的概念,只是在編譯時,把這些切面織入進去。對于pojo而言完全是透明的。是不是很6。不過這里也有很多其他的性能瓶頸,比如說我在第一次調用時,要先去數據庫fetch,這也是耗時操作。這個先跳過,有機會再跟大家八一八,我那數據庫框架是怎么擼出來的。

適配器模式

如果你已經看過retrofit源碼,很可能被 CallAdapter 玩壞。這個 CallAdapter 不是那么好理解。先拋開代碼,我們來看看適配器模式。

Adapter簡單來說,就是將一個已存在的東西轉換成適合我們使用的東西。就比方說電源Adapter。出國旅游都要帶轉接頭。比方說, RecyclerView 里的Adapter是這么定義的。 Adapters provide a binding from an app-specific data set to views。

再回來看看Retrofit,為什么我們需要轉接頭呢。那個被轉換的是誰?我們看看 CallAdapter 的定義。 Adapts a {@link Call} into the type of {@code T}. 這個Call是OkHttpCall,它不能被我們直接使用嗎?被轉換后要去實現什么特殊的功能嗎?

我們假設下。一開始,retrofit只打算在android上使用,那就通過靜態代理 ExecutorCallbackCall 來切換線程。但是后來發現rxjava挺好用啊,這樣就不需要Handler來切換線程了嘛。想要實現,那得轉換一下。將 OkHttpCall 轉換成rxjava( Scheduler )的寫法。再后來又支持了java8( CompletableFuture )甚至居然還有iOS支持。大概就是這樣一個套路。當然我相信square的大神肯定一開始就考慮了這種情況,從而設計了 CallAdapter 。

適配器模式就是,已經存在的 OkHttpCall ,要被不同的標準,平臺來調用。設計了一個接口 CallAdapter ,讓其他平臺都是做不同的實現來轉換,這樣不花很大的代價就能再兼容一個平臺。666。

策略模式?

在retrofit里,這個適配器模式不是那么明顯。而且和其他模式交錯在一起,所以看起來很麻煩。比如這個CallAdapter又夾雜著策略模式(僅是個人看法)。你可以看看Rxjava里如何去創建adapter的,它是根據api方法聲明的returnType來創建具體的 CallAdapter 實例的。上代碼你就明白了。

是不是很像根據不同的策略使用不同的算法?不同的returnType聲明就是set不同的Strategy。

總結

來張提綱挈領的流程圖,沒保存的趕緊存起來。以后就能照著它自己開車了。

好,大概就將這么多啦。這些就是retrofit的解耦套路了。通過一系列的設計模式,封裝思想來解耦,看到現在,其實retrofit就是一個負責調度的controller。先給retrofit配置好,讓它能夠正常工作。你給它一個方法調用,它就在內部開始運轉。這個方法以前我消化過嗎,沒消化那就用一個ServiceMethod來解析它。解析后要用來配置一個request請求。但它自己搞不定這事啊,所以需要給它一個轉接頭,通過轉接頭來使用okhttpcall。請求是做好了,但是response它又不認識,所以又請來convertor來幫忙,轉換完畢之后才吐出一個我們最終要的那個對象。

終點站到啦,請下車。覺得老司機開的穩,坐的舒心。不妨再刷個卡吧。。滴滴。。

如果看文章不夠過癮,可以看Stay精心錄制的視頻 Retrofit分析-漂亮的解耦套路 ,看完你再也不怕看不懂retrofit了。而且你還可以用Stay這種分析套路來輕松看懂其他源碼。

來源:http://notes.stay4it.com/2016/05/04/design-pattern-in-retrofit/

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