本文目錄如下
網絡子產品需要具備什麼能力 為什麼Retrofit是個好選擇 Retrofit業務分析 Retrofit技術點 設計模式
常見的需求下圖:
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIn5GcucjY3YTYhFTZhRDZkJTYzEWYwkTMkNWMwY2NxIjYykTMvwVbvNmLj5Wat4Wd5lGbh5iY1BXLn1WauU3bop3ZuFGat42YucWbp1iMhRXYvw1LcpDc0RHaiojIsJye.png)
下面解釋一下重要的部分.
為了讓頁面快速展現,很多頁面需要先加載緩存.
通用政策是
1.先加載緩存;
2.然後做網絡請求
3.網絡請求成功後重新整理頁面,并且更新緩存資料
這裡涉及到,緩存要存在哪裡. 一般也是兩種方案:
業務側做緩存. 每個頁面自己維護緩存,一般存在db/sp/檔案系統中 網絡側做緩存, 業務做網絡請求的時候,可以要求傳回緩存資料.
方案1:适用于存儲需要檢索資料的情況. 比如資料存在db中,可以用sql查詢. 缺點是業務側需要實作緩存政策,較麻煩.
方案2: 業務側調用簡單,适用場景非常廣泛.
是以在網路側支援緩存, 是一個很普遍的方案.
這個是基于這幾種前提.
App有登入功能 App登入通過 AccessToken/RefreshToken實作
簡單說明 AccessToken失效後會發生什麼:
AccessToken失效
做網絡請求,服務端會傳回結果說AccessToken失效
此時網絡請求是失敗了
用戶端利用RefreshToken 重新換AccessToken.
拿着新AccessToken 重發網絡請求.
業務側拿到正确結果
一般AccessToken失效是比較頻繁的, 如果沒有自動重新請求機制, 每次AccessToken失效使用者都能感覺到,這樣體驗很不好. 是以一般要在網絡庫/登入子產品之間或者上層做一個封裝解決這個問題..
最好是能:
取消一次網絡請求
某個頁面退出時,能取消整個頁面對應的網絡請求.
資源嗎,能省點就省點
很多請求是需要需要刷UI的, 這樣就不需要主動post到UI線程了.
選開源庫,有幾個點很重要.
成熟的項目明顯的坑少. 是以一個應用廣泛的庫是一個穩妥的選擇.
Retrofit: Square出品, 2萬+ star,一直在更新
是代碼總會有bug的, 有問題要能看明白,可以選擇規避或者自行解決. 代碼龐大或者邏輯複雜時,排查問題就是一個很痛苦的過程
Retrofit 代碼量少,結構清晰
業務是變化的, 要在預期的業務變化内, 目前庫要能cover住, 如果開發幾版發現不能滿足拓展的需求, 臨時換庫,那會帶來很多工作量和bug
Retrofit 在解耦上做的很棒,拓展性高
在這點上很重要,引入一個庫如果要一并帶入很多它的依賴庫,會有很大的問題:
會增大包的體積
會讓庫變得不好維護,鍊條上的每個庫都可能有問題.
最大的問題是, 這些依賴庫可能會和App的其他依賴庫沖突. 會産生類沖突,版本沖突. 排查這些沖突是一個很痛苦的過程.(在這點上, 集團的依賴庫這個問題尤為嚴重)
Retrofit目前依賴于Okhttp3, 考慮到OKhttp3也很流行,可以一并引入
做為一個網絡庫Okhttp的封裝, Retrofit需要處理3部分問題:
允許業務拓展哪些環節. 這些拓展的注冊,和什麼情況下生效 拓展是否靈活
Retrofit允許配置的是 CallAdapter,RequestBodyConverter,ResponseConvert.
CallAdapter: 負責調用OkHttpCall發起請求,處理回調分發 RequestBodyConverter: 在建構Request的時候, 建構body ResponseConverter: 轉換網絡請求結果.
Retrofit發起網絡請求最終走的的OKHttpCall.
網絡請求的流程套路大家都一樣, 都是:
"業務發起請求"->"建構Request"->"發起請求"->"解釋請求"->"回調業務".
下面是他的調用流程, 藍色部分是自定義子產品.
在Retrofit初始化的時候, 允許添加CallAdapterFactory和ConverterFactory.
注冊和擷取的流程如下.
按注冊的順序擷取,隻要找到了就就結束查找.
每一個請求聲明是一個函數, 他包含3部分資訊:
Return Type Method Annotation Parameter Annotation
對于CallAdapter/RequestBodyConverter/ResponseConvert可以擷取到這3部分資訊.然後做相應操作,過程是非常靈活.
關于Type下面這個文章講的比較好
<a href="http://blog.csdn.net/yuanyang5917/article/details/53130187">Retrofit完全解析(三):Type<基礎詳解></a>
對于方法
方法有三部分資訊
函數傳回值
方法體上的Annotaion
參數中的Annotation
Retrofit中根據這3部分資訊, 比對CallAdapter/Converter,生成ServiceMethod, 中間用了大量的泛型解釋.代碼集中展現在 Utils/ServiceMethod/CallAdapter/Converter上.
1.不能修改的類用final修飾
2.new ConcurrentHashMap<>()處理并發,取得時候加上synchronized判斷
3.adapterFactories = unmodifiableList(adapterFactories); // Defensive copy at call site. 做參數保護
<a href="https://github.com/square/okhttp/tree/master/mockwebserver">MockWebServer-github</a>
com.squareup.okhttp3:mockwebserver
比較強大的網絡模拟工具, 配合Okhttp用很友善
作者: stay4it
<code>Retrofit</code>給我們暴露的方法和類不多。核心類就是<code>Retrofit</code>,我們隻管配置<code>Retrofit</code>,然後做請求。剩下的事情就跟上層無關了,隻需要等待回調。這樣大大降低了系統的耦合度。對于這種寫法,我們叫外觀模式(門面模式)。
幾乎所有優秀的開源library都有一個門面。比如<code>Glide.with()</code>, <code>ImageLoader.load()</code>, <code>Alamofire.request()</code>。有個門面友善記憶,學習成本低,利于推廣品牌。 Retrofit的門面就是<code>retrofit.create()</code>
當我們自己寫的代碼的時候盡量也要這樣來做。
比如我們有一個獨立并公用的子產品,需要供其他子產品來調用。比如download,location,socialshare等
最好我們寫一個module,将所有相關的代碼都放在這個module中。這是第一步。
第二步,為你的module提供一個漂亮的門面。比如下載下傳的DownloadManager, 經緯度的LocationTracker, 社交分享的SocialManager。它們做為功能子產品的入口,要盡量的簡潔,方法命名好記易了解,類上要有完整的示例注釋
第三步,閉門造車。不管你在裡面幹什麼,外面都是不知道的,就像薛定谔的那隻貓,外層不調用它,永遠不知道它是否好用。
不過為了以後好維護,不給他人留坑,還是盡量寫的工整一些。
<a href="http://www.jianshu.com/p/2f518a4a4c2b">Java靜态代理和動态代理介紹</a>
再來說動态代理。以往的動态代理和靜态代理使用的場景是類似的。都想在delegate調用方法前後做一些操作。如果我的代理類有很多方法,那我得額外寫很多代碼,是以這時候就引入了動态代理。通過動态設定delegate,可以處理不同代理的不同方法。看不懂沒關系,直接上代碼:
内部<code>Converter,CallAdapter</code>都用了抽象工廠模式, <code>Interface</code>和<code>Factory</code>放到了一個類中,減少類檔案的數量, Factory生成對應Interface的類,明确比對關系.
如果你已經看過retrofit源碼,很可能被<code>CallAdapter</code>玩壞。這個<code>CallAdapter</code>不是那麼好了解。先抛開代碼,我們來看看擴充卡模式。
<code>Adapter</code>簡單來說,就是将一個已存在的東西轉換成适合我們使用的東西。就比方說電源<code>Adapter</code>。出國旅遊都要帶轉接頭。比方說,<code>RecyclerView</code>裡的<code>Adapter</code>是這麼定義的。 <code>Adapters provide a binding from an app-specific data set to views</code>。
再回來看看<code>Retrofit</code>,為什麼我們需要轉接頭呢。那個被轉換的是誰?我們看看<code>CallAdapter</code>的定義。<code>Adapts a {@link Call} into the type of {@code T}</code>. 這個<code>Call</code>是<code>OkHttpCall</code>,它不能被我們直接使用嗎?被轉換後要去實作什麼特殊的功能嗎?
我們假設下。一開始,<code>retrofit</code>隻打算在<code>android</code>上使用,那就通過靜态代理<code>ExecutorCallbackCall</code>來切換線程。但是後來發現<code>rxjava</code>挺好用啊,這樣就不需要<code>Handler</code>來切換線程了嘛。想要實作,那得轉換一下。将<code>OkHttpCall</code>轉換成<code>rxjava(Scheduler)</code>的寫法。再後來又支援<code>java8(CompletableFuture)</code>甚至居然還有iOS支援。大概就是這樣一個套路。當然我相信square的大神肯定一開始就考慮了這種情況,進而設計了<code>CallAdapter。</code>
擴充卡模式就是,已經存在的OkHttpCall,要被不同的标準,平台來調用。設計了一個接口CallAdapter,讓其他平台都是做不同的實作來轉換,這樣不花很大的代價就能再相容一個平台。666。
裝飾模式跟靜态代理很像。
每次一說裝飾模式,就想成decorator,實際上叫wrapper更直覺些。既然是wrapper,那就得有源的句柄,在構造wrapper時得把source作為參數傳進來。wrapper了source,同樣還wrapper其他功能。
代理模式,Proxy Delegate,實際上Delegate也不知道自己被代理了,Proxy僞裝成Delegate來執行,既然是proxy,那proxy不應該提供delegate沒有的public方法,以免被認出來。
抛開理論的描述,我們直接來看下面的代碼。
你可以将ExecutorCallbackCall當作是Wrapper,而真正去執行請求的源Source是OkHttpCall。之是以要有個Wrapper類,是希望在源Source操作時去做一些額外操作。這裡的操作就是線程轉換,将子線程切換到主線程上去。
<a href="http://www.jianshu.com/p/45cb536be2f4">Retrofit分析-漂亮的解耦套路</a>
<a href="http://www.jianshu.com/p/fb8d21978e38">Retrofit分析-經典設計模式案例</a>
<a href="http://www.jianshu.com/p/050c6db5af5a">Android主流網絡請求開源庫的對比(Android-Async-Http、Volley、OkHttp、Retrofit)</a>
<a href="http://www.jianshu.com/p/0c055ad46b6c">Android:手把手帶你深入剖析 Retrofit 2.0 源碼</a>