Retrofit2之源碼解析2

4.從架構角度看Retrofit的做用、原理和啓示
3.Retrofit 框架源碼學習
2.Retrofit分析-經典設計模式案例
1.Retrofit分析-漂亮的解耦套路php

=================css

4.從架構角度看Retrofit的做用、原理和啓示java

Retrofit是squareup公司的開源力做,和同屬squareup公司開源的OkHttp,一個負責網絡調度,一個負責網絡執行,爲Android開發者提供了即方便又高效的網絡訪問框架。android

不過,對於Retrofit這樣設計精妙、代碼簡潔、使用方便的優秀開源項目,不能僅知道如何擴展和使用,或者僅研究它採用的技術或模式,git

「技」固然重要,但不能忽視了背後的「道」。github

對於Retrofit,咱們還應該看到的,是她在優化App架構方面的努力,以及她在提高開發效率方面的借鑑和啓示。算法

本文試圖經過一個具體場景,先總結Retrofit在架構中起到的做用,再分析其實現原理,最後探討Retrofit給咱們帶來的啓示。數據庫

咱們先經過一個簡單的應用場景來回顧Retrofit的使用過程。編程

基本場景

一般來講,使用Retrofit要通過這樣幾個步驟json

  1. 引用
compile 'com.squareup.retrofit2:retrofit:2.3.0'
    compile 'com.squareup.retrofit2:retrofit-converters:2.3.0'
    compile 'com.squareup.retrofit2:retrofit-adapters:2.3.0'

若是須要使用更多擴展功能,好比gson轉換,rxjava適配等,能夠視本身須要繼續添加引用

compile 'com.squareup.retrofit2:converter-gson:2.3.0'
    compile 'com.squareup.retrofit2:adapter-rxjava2:2.3.0'

若是現有的擴展包不能知足須要,還能夠本身擴展converter,adapter等。

  1. 定義接口
    Retrofit要求定義一個網絡請求的接口,接口函數裏要定義url路徑、請求參數、返回類型。
public interface INetApiService { @GET("/demobiz/api.php") Call<BizEntity> getBizInfo(@Query("id") String id); } 

在這個接口定義中,用註解@GET("/demobiz/api.php")聲明瞭url路徑,用註解@Query("id") 聲明瞭請求參數。
最重要的是,用Call<BizEntity>聲明瞭返回值是一個Retrofit的Call對象,而且聲明瞭這個對象處理的數據類型爲BizEntity,BizEntity是咱們自定義的數據模型。

  1. 依次得到Retrofit對象、接口實例對象、網絡工做對象
    首先,須要新建一個retrofit對象。
    而後,根據上一步的接口,實現一個retrofit加工過的接口對象。
    最後,調用接口函數,獲得一個能夠執行網絡訪問的網絡工做對象。
//新建一個Retrofit對象 Retrofit retrofit=new Retrofit.Builder() .baseUrl(Config.DOMAIN)//要訪問的網絡地址域名,如http://www.zhihu.com .addConverterFactory(GsonConverterFactory.create()) .build(); ... //用retrofit加工出對應的接口實例對象 INetApiService netApiService= retrofit.create(INetApiService.class); //能夠繼續加工出其餘接口實例對象 IOtherService otherService= retrofit.create(IOtherService.class); ··· //調用接口函數,得到網絡工做對象 Call<BizEntity> callWorker= netApiService.getBizInfo("id001"); 

這個複雜的過程下來,最終獲得的callWorker對象,才能夠執行網絡訪問。

  1. 訪問網絡數據
    用上一步獲取的worker對象,執行網絡請求
callWorker.enqueue(new Callback<BizEntity>() { @Override public void onResponse(Call<BizEntity> call, Response<BizEntity> response) {...} @Override public void onFailure(Call<BizEntity> call, Throwable t) {...} }); 

在回調函數裏,取得咱們須要的BizEntity數據對象。
網絡訪問結束。

角色與做用

咱們從上面的應用場景能夠看出,Retrofit並不作網絡請求,只是生成一個能作網絡請求的對象。
Retrofit的做用是按照接口去定製Call網絡工做對象。

什麼意思?就是說:
Retrofit不直接作網絡請求
Retrofit不直接作網絡請求
Retrofit不直接作網絡請求

重要的事情說三遍。

網絡請求的目標雖然是數據,可是咱們須要爲這個數據寫大量的配套代碼,發起請求的對象Call,接收數據的對象CallBack,作數據轉換的對象Converter,以及檢查和處理異常的對象等。
這對於一個項目的開發、擴展和維護來講,都是成本和風險。

而Retrofit作的事情,就是爲開發者節省這部分的工做量,Retrofit一方面從底層統一用OkHttp去作網絡處理;

另外一方面在外層靈活提供能直接融入業務邏輯的Call網絡訪問對象。

具體來講,Retrofit只負責生產對象,生產能作網絡請求的工做對象,他有點像一個工廠,只提供產品,工廠自己不處理網絡請求,產品才能處理網絡請求。
Retrofit在網絡請求中的做用大概能夠這樣理解:


 
Retrofit的做用

咱們看到,從一開始,Retrofit要提供的就是個Call工做對象。
換句話說,對於給Retrofit提供的那個接口

public interface INetApiService { @GET("/demobiz/api.php") Call<BizEntity> getBizInfo(@Query("id") String id); } 

這個接口並非傳統意義上的網絡請求接口,這個接口不是用來獲取數據的接口,而是用來生產對象的接口,這個接口至關於一個工廠,接口中每一個函數的返回值不是網絡數據,而是一個能進行網絡請求的工做對象,咱們要先調用函數得到工做對象,再用這個工做對象去請求網絡數據。

因此Retrofit的實用價值意義在於,他能根據你的接口定義,靈活地生成對應的網絡工做對象,而後你再擇機去調用這個對象訪問網絡。
理解了這一點,咱們才能去擴展Retrofit,並理解Retrofit的設計思想。

功能擴展

咱們先來看Retrofit能擴展哪些功能,而後再去理解Retrofit的工做原理。
Retrofit主要能夠擴展三個地方:

  1. OkHttpClient
    Retrofit使用OkHttpClient來實現網絡請求,這個OkHttpClient雖然不能替換爲其餘的網絡執行框架好比Volley,可是Retrofit容許咱們使用本身擴展OkHttpClient,通常最常擴展的就是Interceptor攔截器了
OkHttpClient mClient = new OkHttpClient.Builder() .addInterceptor(new Interceptor() { @Override public Response intercept(Chain chain) throws IOException { try { Request.Builder builder = chain.request().newBuilder(); builder.addHeader("Accept-Charset", "UTF-8"); builder.addHeader("Accept", " application/json"); builder.addHeader("Content-type", "application/json"); Request request = builder.build(); return chain.proceed(request); } catch (Exception e) { e.printStackTrace(); } return null; } }).build(); Retrofit retrofit = new Retrofit.Builder() .baseUrl(Config.DOMAIN) .addConverterFactory(GsonConverterFactory.create()) .client(mClient) .build(); 
  1. addConverterFactory

擴展的是對返回的數據類型的自動轉換,把一種數據對象轉換爲另外一種數據對象。
在上述場景中,GsonConverterFactory能夠把Http訪問獲得的json字符串轉換爲Java數據對象BizEntity,這個BizEntity是在INetApiService接口中要求的的。
這種轉換咱們本身也常常作,很好理解。
若是現有的擴展包不能知足須要,能夠繼承Retrofit的接口。retrofit2.Converter<F,T>,本身實現Converter和ConverterFactory。
在建立Retrofit對象時,能夠插入咱們自定義的ConverterFactory。

//retrofit對象 Retrofit retrofit=new Retrofit.Builder() .baseUrl(Config.DOMAIN) .addConverterFactory(GsonConverterFactory.create()) .addConverterFactory(YourConverterFactory.create())//添加自定義Converter .build(); 
  1. addCallAdapterFactory

        擴展的是對網絡工做對象callWorker的自動轉換,把Retrofit中執行網絡請求的Call對象,轉換爲接口中定義的Call對象。
這個轉換不太好理解,咱們能夠對照下圖來理解:


 
callAdapter轉換Call對象

Retrofit自己用一個OkHttpCall的類負責處理網絡請求,而咱們在接口中定義須要定義不少種Call,例如Call<BizEntity>,或者Flowable<BizEntity>等,接口裏的Call和Retrofit裏的OkHttpCall並不一致,因此咱們須要用一個CallAdapter去作一個適配轉換。
(Retrofit底層雖然使用了OkHttpClient去處理網絡請求,但她並無使用okhttp3.call這個Call接口,而是本身又建了一個retrofit2.Call接口,OkHttpCall繼承的是retrofit2.Call,與okhttp3.call只是引用關係。
這樣的設計符合依賴倒置原則,能夠儘量的與OkHttpClient解耦。)

這實際上是Retrofit很是核心,也很是好用的一個設計,若是咱們在接口中要求的函數返回值是個RxJava的Flowable對象

public interface INetApiService { @GET("/demobiz/api.php") Flowable<BizEntity> getBizInfo(@Query("id") String id); } 

那麼咱們只須要爲Retrofit添加對應的擴展

//retrofit對象 Retrofit retrofit=new Retrofit.Builder() .baseUrl(Config.DOMAIN) .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .build(); 

就能獲得Flowable類型的callWorker對象

//用retrofit加工出對應的接口實例對象 INetApiService netApiService= retrofit.create(INetApiService.class); ··· //調用接口函數,得到網絡工做對象 Flowable<BizEntity> callWorker= netApiService.getBizInfo("id001"); 

在這裏,callAdapter作的事情就是把retrofit2.Call對象適配轉換爲Flowable<T>對象。
一樣,若是現有的擴展包不能知足須要,能夠繼承Retrofit的接口retrofit2.CallAdapter<R,T>,本身實現CallAdapter和CallAdapterFactory。

Retrofit實現原理

Retrofit當然設計精妙,代碼簡潔,使用方便,但相應的,咱們要理解Retrofit的實現原理也不太容易,這麼精妙的設計是極佳的研究素材,咱們不能僅僅停留在知道怎麼使用,怎麼擴展的階段,那實在是對這個優秀開源項目的浪費。
其實,Retrofit使用的,就是動態代理,方法註解、建造者和適配器等成熟的技術或模式,可是因爲她的設計緊湊,並且動態代理屏蔽了不少過程上的細節,因此比較難以理解。

Retrofit實現原理——從動態代理開始

從前面的使用場景可知,retrofit會生成一個接口實例。

//用retrofit加工出對應的接口實例對象 INetApiService netApiService= retrofit.create(INetApiService.class); 

到Retrofit源碼裏看create函數,是一個動態代理。

public <T> T create(final Class<T> service) { ... return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service }, new InvocationHandler() { ... ServiceMethod<Object, Object> serviceMethod = (ServiceMethod<Object, Object>) loadServiceMethod(method); OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args); return serviceMethod.callAdapter.adapt(okHttpCall); } }); } 

要理解動態代理,最好要看到動態生成的代理類。

因爲動態代理是在運行時動態生成的代理類,用常規的反編譯方法沒法查看,通常要使用Java提供的sun.misc.ProxyGenerator.generateProxyClass(String proxyName,class[] interfaces)函數生成代理類,函數會返回byte[]字節碼,而後對字節碼反編譯獲得Java代碼。
有一個小問題是,AndroidStudio並不提供sun.misc這個包,咱們須要用IntelliJ或者Eclipse創建一個Java工程,在Java環境裏調用這個函數。

拿到的代理類,大概是這樣的:

public final class INetApiService extends Proxy implements INetApiService { ...//一些Object自帶方法 private static Method m3;//接口定義的方法 static { try { //Object自帶方法的初始化 m0,m1,m2 = ... //接口中定義的方法 m3 = Class.forName("com.demo.net$INetApiService")//反射接口類 .getMethod("getBizInfo",//反射函數 new Class[] { Class.forName("java.lang.String") });//反射參數 //接口中定義的其餘方法 ... } ... } //返回接口實例對象 public INetApiService (InvocationHandler invocationHandler){ super(invocationHandler); } // public final Call getBizInfo(String str){ ... try{//用Handler去調用 return (Call)this.h.invoke(this, m3, new Object[]{str}); } } } 

咱們能夠看到,代理類生成的是一個INetApiService接口的實例對象,該對象的getBizInfo函數返回的是接口中定義的Call網絡工做對象,

這也體現了Retrofit的核心價值,生成接口定義的Call網絡工做對象。

那麼,這個Call網絡工做對象是如何生成的呢,上面動態代理生成的代碼是這樣的:

return (Call)this.h.invoke(this, m3, new Object[]{str}); 

也就是說,這個Call網絡工做對象是在InvocationHandler中實現的,也就是在Retrofit.create函數中,由InvocationHandler實現的。

這樣咱們就明白了,Retrofit使用動態代理,實際上是爲了開發者在寫代碼時方便調用,而真正負責生產Call網絡工做對象的,仍是Retrofit.create函數中定義的這個InvocationHandler,這個InvocationHandler的代碼咱們再貼一遍:

new InvocationHandler() { ... ServiceMethod<Object, Object> serviceMethod = (ServiceMethod<Object, Object>) loadServiceMethod(method); OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args); return serviceMethod.callAdapter.adapt(okHttpCall); } 

ServiceMethod能讓咱們準確解析到INetApiService中定義的函數,爲最後的適配轉換提供轉換目標,詳細分析咱們後面再說,先看適配轉換的過程。

咱們看到,Retrofit內部默認使用OkHttpCall對象去處理網絡請求,可是返回的網絡工做對象是通過適配器轉換的,轉換成接口定義的那種Call網絡工做對象。

這個適配轉換,就是Retrofit能按照接口去定製Call網絡工做對象的祕密。

Retrofit實現原理——適配轉換Call對象

咱們在初始化Retrofit對象時,好像不添加CallAdapterFactory也能實現適配轉換。

//retrofit對象 Retrofit retrofit=new Retrofit.Builder() .baseUrl(Config.DOMAIN) .addConverterFactory(GsonConverterFactory.create()) //能夠不添加CallAdapterFactory .build(); 

這是怎麼回事呢,咱們知道Retrofit使用了建造者模式,建造者模式的特定就是實現了建造和使用的分離,因此建造者模式的建造函數裏,

通常會有很複雜的對象建立和初始化過程,因此咱們要看一下Retrofit的build函數。

public Retrofit build() { ... okhttp3.Call.Factory callFactory = this.callFactory; if (callFactory == null) { callFactory = new OkHttpClient();//使用OkHttpClient處理網絡請求 } ... //根據當前運行平臺,設置默認的callAdapterFactory adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor)); ... return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories, callbackExecutor, validateEagerly); } 

這段代碼裏,咱們看到Retrofit使用OkHttpClient處理網絡請求,而且會添加默認的callAdapterFactory,這個platform是一個簡單工廠,能根據當前系統平臺去生成對應的callAdapterFactory

private static Platform findPlatform() { try { Class.forName("android.os.Build"); if (Build.VERSION.SDK_INT != 0) { return new Android();//根據當前系統平臺返回相應的對象 } ... } ... static class Android extends Platform { ... @Override CallAdapter.Factory defaultCallAdapterFactory(@Nullable Executor callbackExecutor) { if (callbackExecutor == null) throw new AssertionError(); return new ExecutorCallAdapterFactory(callbackExecutor); } ... } 

這個Platform是Retrofit在Builder的構造函數裏初始化的。

因此,在Retrofit.build()函數中,咱們爲Retrofit默認添加的callAdapterFactory,是在Platform中爲Android系統設定的ExecutorCallAdapterFactory。
咱們看ExecutorCallAdapterFactory的代碼,這是一個工廠類,能夠返回CallAdapter對象:

@Override
  public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) { ... return new CallAdapter<Object, Call<?>>() { ... // 轉換後 轉換前,也就是OkHttpCall @Override public Call<Object> adapt(Call<Object> call) { return new ExecutorCallbackCall<>(callbackExecutor, call); } }; } 

在adapt函數中,適配器會把Retrofit中用來訪問網絡的OkHttpCall,轉換爲一個ExecutorCallbackCall(繼承了INetApiService接口裏要求返回的網絡工做對象retrofit2.Call),
這個例子裏面,因爲OkHttpCall和ExecutorCallbackCall都實現了retrofit2.Call接口,結果出現了從Call<Object>轉換爲Call<Object>的狀況,這可能不容易理解,咱們換個RxJava2CallAdapterFactory來看看

//RxJava2CallAdapterFactory中 @Override public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) { ... return new RxJava2CallAdapter(responseType, scheduler, isAsync, isResult, isBody, isFlowable, isSingle, isMaybe, false); } //RxJava2CallAdapter中 // 轉換後 轉換前,也就是OkHttpCall @Override public Object adapt(Call<R> call) { ... Observable<?> observable; ... return observable; } 

這個CallAdapter的轉換就比較明顯了,把retrofit2.Call對象經過適配器轉換爲了一個實爲Observable<?>的Object對象。

至此,咱們能夠理解Retrofit根據接口定義動態生產Call網絡請求工做對象的原理了,其實就是經過適配器把retrofit2.Call對象轉換爲目標對象。

至於適配器轉換過程當中,如何實現的對象轉換,就能夠根據需求來自由實現了,好比利用靜態代理等,若有必要,咱們能夠自行開發擴展,Retrofit框架並不限制咱們對於適配器的實現方式。

Retrofit實現原理——函數解析、網絡請求和數據轉換

在前面分析中,咱們知道了Retrofit的總體工做流程,就是Retrofit用動態代理生成Call網絡請求對象,在這個過程當中,用適配器把Retrofit底層的retrofit2.Call對象轉換爲INetApiService中定義的Call網絡請求對象(如Flowable)。

問題是,Retrofit具體是如何知道了INetApiService中定義的Call網絡請求對象,如何實現網絡請求,以及如何執行的數據轉換呢?

具體過程以下;
首先,根據INetApiService中定義的函數,解析函數,獲得函數的具體定義,並生成對應的ServiceMethod。
而後,根據這個ServiceMethod,實現一個OkHttpCall的Call對象,負責在Retrofit底層實現網絡訪問。
其中,在網絡訪問返回了網絡數據時,根據ServiceMethod實現數據轉換。
最後,利用上一小節中匹配的適配器,把OkHttpCall對象轉換爲INetApiService要求的Call網絡請求對象。

因此,咱們要了解的就是函數解析、網絡請求和數據轉換這三個動做,至於最後的適配轉換,在上一節中已經分析過了。

1. 函數解析
在接口函數裏,用註解描述了輸入參數,用Java對象定義了返回值類型,因此對輸入參數和返回值,ServiceMethod採起了不一樣的方式去處理。
輸入參數
輸入參數是用來描述url的,它的處理相對簡單,ServiceMethod會根據反射獲得的Method,取得Annotation註解信息,這些註解是Retrofit本身預約義好的(retrofit2.http.*),ServiceMethod根據預先的定義,直接判斷註解所屬的邏輯分支,在有網絡請求時分狀況進行處理,就能獲得目標url,http請求頭等數據。
返回值
返回值是須要用CallAdapter去適配的,因此核心在於生成對應的CallAdapter。
在Retrofit生成Call網絡工做對象時,她經過動態代理獲取到了接口函數的Method定義,從這個Method中能夠獲取函數定義的返回對象類型,因爲這個轉換是須要CallAdapterFactory生產CallAdapter對象去實現,而Retrofit事先並不知道要使用哪一個Factory,因此她是遍歷全部的CallAdapterFactory,根據目標函數的返回值類型,讓每一個Factory都去嘗試生產一個CallAdapter,哪一個成功就用哪一個。

2. 網絡請求
OkHttpCall繼承的retrofit2.Call接口是爲了依賴倒置解耦的,真正的網絡請求是由OkHttpCall內部引用的okhttp3.call處理的,這個okhttp3.call是
借道ServiceMethod獲取的Retrofit中的callFactory,也就是Retrofit中的OkHttpClient。

整個引用鏈條是這樣的:
OkHttpCall--okhttp3.call
-->
ServiceMethod--callFactory
-->
Retrofit.build()--callFactory//(如未擴展賦值)new OkHttpClient();
-->
Retrofit.Builder().client(mClient)//(可能有擴展賦值)擴展過的OkHttpClient

最終的網絡請求是由OkHttpCall調用OkHttpClient發出的,調用和回調等過程,也就是在OkHttpCall中處理的。

網絡請求的生成過程當中,爲了使用接口函數中定義的參數,OkHttpCall會調用ServiceMethod來生成Request請求對象,再交給OkHttpCall去處理。

3. 數據轉換
由於回調是在OkHttpCall中處理的,因此對回調數據的轉換也在OkHttpCall中觸發,爲了符合接口函數中定義的返回數據類型,OkHttpCall會調用ServiceMethod來轉換Response返回數據對象。

OkHttpCall對返回的網絡數據,會調用一個serviceMethod.toResponse(ResponseBody body)函數,函數中執行的是:

R toResponse(ResponseBody body) throws IOException { return responseConverter.convert(body); } 

這個函數能夠把原始的okhttp3. ResponseBody數據轉換爲INetApiService接口中要求的數據類型(如BizEntity類型)。
從代碼能夠看出,實現數據轉換的核心對象實際上是responseConverter,這個Converter實際上要依次通過Retrofit的建造和ServiceMethod的建造後,才能肯定下來的。

Retrofit建造時添加數據轉換工廠
Retrofit裏有converterFactries列表,這是在咱們初始化Retrofit實例時添加的

//retrofit對象 Retrofit retrofit=new Retrofit.Builder() .baseUrl(Config.DOMAIN) .addConverterFactory(GsonConverterFactory.create()) .addConverterFactory(YourConverterFactory.create())//添加自定義Converter .build(); 

ServiceMethod建造時設定數據轉換器
ServiceMethod在建造時,就已經肯定了對應的是INetApiService中的哪一個函數,因此須要明確設定本身的Converter<R,T>轉換對象

public ServiceMethod build() { ... responseConverter = createResponseConverter(); ... } 

這須要調用Retrofit

private Converter<ResponseBody, T> createResponseConverter() { ... retrofit.responseBodyConverter(responseType, annotations); } 

Retrofit會在本身的轉換器工廠列表中遍歷每一個ConverterFactory,嘗試根據ServiceMethod所對應的目標數據類型,找到Converter數據轉換類

for (int i = start, count = converterFactories.size(); i < count; i++) { Converter<ResponseBody, ?> converter = converterFactories.get(i).responseBodyConverter(type, annotations, this); if (converter != null) { //noinspection unchecked return (Converter<ResponseBody, T>) converter; } } 

以Gson轉換爲例,GsonConverterFactory會經過getAdapter來嘗試匹配目標數據類型:

public <T> TypeAdapter<T> getAdapter(TypeToken<T> type) {...} 

若是能夠匹配,那麼前面調用serviceMethod.toResponse(ResponseBody body)函數時,會調用

R toResponse(ResponseBody body) throws IOException { return responseConverter.convert(body); } 

在調用這段代碼時,其實就是調用了Gson中最終執行數據轉換的代碼:

@Override public T convert(ResponseBody value) throws IOException { JsonReader jsonReader = gson.newJsonReader(value.charStream()); try { return adapter.read(jsonReader); } finally { value.close(); } } 

總結來講,Retrofit在類的單一職責方面分隔的很好,OkHttpCall類只負責網絡交互,凡是須要知道函數定義的,都交給ServiceMethod類去處理,而ServiceMethod類對使用者不公開,由於Retrofit是個外觀模式,而全部須要擴展的都在Retrofit的建造者中實現,他們的分工大概是這樣的:


 
三個類的分工

這三個類分工合做,共同實現了函數解析、網絡訪問和數據轉換,並保留了良好的可擴展性。

Retrofit實現原理——總體結構與分工實現

至此,Retrofit的實現細節就已經基本清楚了,他用動態代理去定製接口定義的Call網絡工做對象,用適配器去把底層的Call對象轉換爲目標Call對象,用函數解析/OkHttpClient/數據轉換等實現對Call對象的適配轉換,並能處理真正的網絡請求。
這裏面涉及的總體結構和角色分工,大概能夠這樣表示:


 
總體結構與角色分工

其中,擴展適配器、擴展數據轉換和擴展OkHttpClient,雖然都是經過Retrofit實現擴展,但真正的使用者是Retrofit內部的ServiceMethod、OkHttpCall和okhttp3.call等類或對象。

反推Retrofit的設計過程

若是咱們不直接正面分析Retrofit的結構設計和技術細節,而是先從Retrofit的功能和做用入手,倒過來推測Retrofit的目標,進而分析其架構和搭建細節,Retrofit爲何會設計成這樣就很好理解了。

Retrofit的功能是按照接口定義,自動定製Call網絡工做對象,因此Retrofit的目標應該就是避免爲網絡訪問開發大量的配套代碼。

爲了實現這一目標,Retrofit須要分析哪些是易變的,哪些是不變的,而後分別處理。

因爲Retrofit提供網絡訪問的工做對象,又是服務於具體業務,因此能夠分網絡訪問和具體業務兩部分來分析。

網絡訪問的不變性
對於網絡訪問來講,不變的是必定有一個實現網絡訪問的對象,Retrofit選用了自家的OkHttpClient,不過爲了把Retrofit和OkHttp兩個項目解耦合,Retrofit根據依賴倒置原則,定義了Retrofit本身的Call即retrofit2.call,並定義了操做網絡請求的OkHttpCall

網絡訪問的易變性
對於網絡訪問來講,易變的是網絡訪問的url、請求方式(get/post等)、Http請求的Header設置與安全設置等,以及返回的數據類型。

針對易變的url和請求方式,Retrofit使用了方法註解的方式,可讀性良好,擴展性優異,但這須要實現對接口函數中註解的解析,這樣就有了ServiceMethod
針對Http請求的各類設置,其實Retrofit沒作什麼,由於Retrofit使用的OkHttp有攔截器機制,能夠應付這種變化。
針對返回的數據類型,因爲目標數據類型與業務有關,是不肯定的,Retrofit沒法提供一個萬能的轉換類,因此Retrofit提供了擴展接口,容許開發者本身定義ConverterFactory和Converter,去實現潛在的數據類型轉換。

具體業務的不變性
對於具體業務來講,不變的是必定要有一個Call網絡工做對象,因此Retrofit能夠有一個生產對象的機制(像工廠同樣)

具體業務的易變性
對於具體業務來講,易變的就是這個Call網絡工做對象的類型,不只有CallBacl回調、可能還有Flowable工做流、或者其餘潛在的對象類型。

針對這種Call對象的易變性,Retrofit也是沒法提供一個萬能的實現類,因此也是提供了擴展解耦,容許開發者本身定義CallAdapterFactory和CallAdapter,去實現潛在的Call類型轉換。

由於這種Call對象的生產須要有大量的配套代碼,爲了簡化代碼,Retrofit使用動態代理來生產這個對象。

最後,由於須要處理的方法和對象太多太複雜,須要使用建造者模式來把建造過程和使用過程分離開。

這樣倒着走一遍以後,咱們再看Retrofit的設計和實現原理,就會以爲水到渠成,對於Retrofit精妙的設計更會有一種切身體會。

借鑑與啓示

在上文的反推過程當中,咱們可窺見(瞎猜)Jake大神的一些思路:

  1. 萬物皆對象
    網絡訪問後,回調數據是個對象;網絡訪問自己也是個對象。
  2. 依賴倒置
    哪怕是使用自家的OkHttp,哪怕底層調用的始終是OkHttpClient,也須要依賴一個抽象的retrofit2.Call接口,依賴於抽象,而不是依賴於具體。
  3. 單一職責
    類的職責須要維持單一,流程須要可是超出本身職責的功能,去調用相關的類實現,好比OkHttpClient和ServiceMethod的各自職責與調用關係。
  4. 迪米特法則
    內部實現再複雜,對於外部調用者也只展現他須要的那些功能,例如Retrofit。
  5. 自動>人工
    動態代理的使用,能夠用自動生成的模板代碼,減輕人工編寫配套代碼的工做量,成本更低,風險更低。
  6. 利用工廠類開放擴展
    對於流程肯定,但方法不能肯定的,利用工廠類,對調用者開放擴展能力。
  7. 利用多個工廠類組成擴展列表
    若是1個工廠類不能實現兼得,何不設置一個工廠類列表,在多個工廠類中,看哪一個工廠類能解決問題。
  8. 利用建造者模式把建造和使用分離
    這樣使用者不須要關係複雜的建造過程,例如Retrofit和ServiceMethod。
  9. 利用外觀模式減小對複雜子系統的操做
    雖然有複雜的子系統協同工做,調用者只須要調用最外層的Retrofit便可。
  10. 其餘
    開放封閉、接口隔離、裏式替換、靜態代理等設計原則或設計模式都有體現也都很熟悉了,就再也不囉嗦。

最後感嘆一下。

對於網絡訪問的抽象與優化,其實是個很是難的課題,在Retrofit以前,你們努力的方向基本上都是Volley/OkHttp這種圍繞底層網絡訪問的工做。
由於越底層的東西越容易抽象,越上升到接近業務層,就越容易在紛擾的業務層中迷失。
Retrofit能精準地抓到Call網絡工做對象這個關鍵點,並能經過一系列精巧的設計實現對這種類型「飄忽不定」的對象的自動化定製生產,着實使人讚歎。

參考

Retrofit
你真的會用Retrofit2嗎?Retrofit2徹底教程
Retrofit2 源碼解析
Retrofit 框架源碼學習
拆輪子系列:拆 Retrofit
Android 動態代理以及利用動態代理實現 ServiceHook

 --------------------

 

3.Retrofit 框架源碼學習

Retrofit,OkHttp,Okio Square 安卓平臺網絡層三板斧源碼學習
基於 retrofit 2.4.0-SNAPSHOT 版本 retrofit github 地址

Retrofit 是 Square 安卓平臺網絡層三板斧最後一個項目,Retrofit 依賴 OkHttp 。Retrofit 讓 http 網絡請求更加清晰。

使用方式

  1. 聲明一個接口,並用接口描述 request

    public interface GitHubService { @GET("users/{user}/repos") Call<List<Repo>> listRepos(@Path("user") String user); } 

    方法上面的註釋表示 request 的接口名 ,方法的返回類型就是 http 請求的返回值,方法的參數就是 http 的請求參數。

  2. 建立一個 Retrofit 客戶端

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

    Retrofit 建立的時候指定了 request 的接口地址,而後調用 retrofit.create 方法建立一個 GitHubService 實例。

  3. 發起網絡請求

    Call<List<Repo>> repos = service.listRepos("octocat"); repos.execute().body() 

Retrofit 建立 Service 實例

上面的例子能夠看到,retrofit.create() 方法會建立一個 GitHubService 實例,可是 GitHubService 自己是一個接口。

爲了瞭解 retrofit.create() 方法,咱們先看下 Retrofit 的建立過程。

建立 Retrofit 對象。

Retrofit 和 OkHttp 同樣都是使用構建者模式建立對象。先看下 Retrofit.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> 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); } 

建立 Retrofit 的時候須要傳遞一下幾個參數

1. callFactory 用來建立一個實現了 okhttp3.Call.Factory 的對象,若是沒有設置,默認爲 OkHttpClient。
2. baseUrl 網絡接口的地址。
3. converterFactories 用來把服務器返回的數據轉換爲對象。
4. adapterFactories 用來發起網絡請求。
5. callbackExecutor 是一個調度器,用來接收返回的數據,在 Android 上默認是封裝了 handler 的 MainThreadExecutor
6. validateEagerly  是一個開關,若是爲 true 會緩存建立的 ServiceMethod 。

retrofit.create()

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(); 

這裏用到了一個公共技術點之 Java 動態代理,create 方法傳入一個 Class ,這個 Class 對象就是上文的 GitHubService 的 Class 。

GitHubService 的方法是由 InvocationHandler 代理實現的,重點看三行代碼

……
ServiceMethod<Object, Object> serviceMethod =(ServiceMethod<Object, Object>) loadServiceMethod(method); OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args); return serviceMethod.callAdapter.adapt(okHttpCall); 
第一行 loadServiceMethod(method)
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; } 

這裏建立了一個 ServiceMethod 對象。

第二行 new OkHttpCall<>(serviceMethod, args)
OkHttpCall(ServiceMethod<T, ?> serviceMethod, @Nullable Object[] args) { this.serviceMethod = serviceMethod; this.args = args; } 

建立了一個 OkHttpCall ,serviceMethod 和 args 是 OkHttpCall 的成員函數。

因此,

第三行 serviceMethod.callAdapter.adapt(okHttpCall)

這裏須要明白 serviceMethod.callAdapter 是怎麼來的

1. 在 ServiceMethod.Builder.build() 中調用 createCallAdapter() 2. 在 createCallAdapter() 中會找到 (CallAdapter<T, R>) retrofit.callAdapter(returnType, annotations) 3. 在 callAdapter() 中調用 nextCallAdapter 4. nextCallAdapter 會遍歷 adapterFactories 返回一個 CallAdapter。 

這裏再回頭看下 adapterFactories Retrofit.Builder.build() 方法中

List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories); adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor)); 

而在 Retrofit.nextCallAdapter() 中

int start = adapterFactories.indexOf(skipPast) + 1; for (int i = start, count = adapterFactories.size(); i < count; i++) { CallAdapter<?, ?> adapter = adapterFactories.get(i).get(returnType, annotations, this); if (adapter != null) { return adapter; } } 

若是沒有設置 AdapterFactory 將會使用一個默認的 AdapterFactory

CallAdapter.Factory defaultCallAdapterFactory(@Nullable Executor callbackExecutor) { if (callbackExecutor != null) { return new ExecutorCallAdapterFactory(callbackExecutor); } return DefaultCallAdapterFactory.INSTANCE; } 

因此若是咱們設置了一個 RxJavaCallAdapterFactory,就會返回 RxJavaCallAdapterFactory。

 
retrofit_01.png

發起網絡請求

經過 retrofit.create() 咱們能夠知道,retrofit.create() 返回的是一個代理對象InvocationHandler ,那麼在執行

Call<List<Repo>> repos = service.listRepos("octocat"); 

方法時,調用的其實是 callAdapter.adapt(okHttpCall),以 DefaultCallAdapterFactory 爲例

結合 retrofit.create() 方法能夠得知這裏返回的是一個 OkHttpCall 對象。

接下來使用 OkHttpCall.execute()  或者 異步執行 enqueue(Callback<T> callback)

這兩種方式都會調用 createRawCall() 建立一個 okhttp3.Call

private okhttp3.Call createRawCall() throws IOException { Request request = serviceMethod.toRequest(args); okhttp3.Call call = serviceMethod.callFactory.newCall(request); if (call == null) { throw new NullPointerException("Call.Factory returned null."); } return call; } 

此處的  serviceMethod.callFactory 就是 retrofit.create() 中建立的 OkHttpClient()
後面的內容都是由 Okhttp 模塊接管,進行網絡請求,參考okHttp 框架源碼學習

而後調用 parseResponse(call.execute())

Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException { ResponseBody rawBody = rawResponse.body(); …… 省略一些 http 返回值處理邏輯 …… try { T body = serviceMethod.toResponse(catchingBody); return Response.success(body, rawResponse); } …… } 

okHttp 請求網絡的返回數據,會交給 serviceMethod.toResponse

R toResponse(ResponseBody body) throws IOException { return responseConverter.convert(body); } 

在 ServiceMethod.Builder.build() 方法中能夠找到 responseConverter 是經過 createResponseConverter() 方法的返回對象。

createResponseConverter() 只是報包裹了 retrofit.responseBodyConverter(responseType, annotations) 方法。

retrofit.responseBodyConverter() 繼續跟蹤下去會得知,返回的是 converterFactories 數組的第 0 個對象,也就是內置的 BuiltInConverters.responseBodyConverter() 方法返回的 BufferingResponseBodyConverter

static final class BufferingResponseBodyConverter implements Converter<ResponseBody, ResponseBody> { static final BufferingResponseBodyConverter INSTANCE = new BufferingResponseBodyConverter(); @Override public ResponseBody convert(ResponseBody value) throws IOException { try { // Buffer the entire body to avoid future I/O. return Utils.buffer(value); } finally { value.close(); } } } 

再看一下 Utils.buffer(value)

static ResponseBody buffer(final ResponseBody body) throws IOException { Buffer buffer = new Buffer(); body.source().readAll(buffer); return ResponseBody.create(body.contentType(), body.contentLength(), buffer); } 

最終會返回一個從新封裝的 Okhttp 框架的 ResponseBody 對象。

 
 

 

 

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

  retrofit就是對okhttp再作了一層封裝。 

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

 

 
retrofit00.png

之前用的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,具體是什麼,咱們下面講具體設計模式時再詳細討論。

 

外觀模式(門面模式)

Retrofit給咱們暴露的方法和類很少。核心類就是Retrofit,咱們只管配置Retrofit,而後作請求。剩下的事情就跟上層無關了,只須要等待回調。這樣大大下降了系統的耦合度。對於這種寫法,咱們叫外觀模式(門面模式)。

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

當咱們本身寫的代碼的時候儘可能也要這樣來作。

好比咱們有一個獨立並公用的模塊,須要供其餘模塊來調用。好比downloadlocationsocialshare
最好咱們寫一個module,將全部相關的代碼都放在這個module中。這是第一步。
第二步,爲你的module提供一個漂亮的門面。好比下載的DownloadManager, 經緯度的LocationTracker, 社交分享的SocialManager。它們作爲功能模塊的入口,要儘可能的簡潔,方法命名好記易理解,類上要有完整的示例註釋
第三步,閉門造車。無論你在裏面幹什麼,外面都是不知道的,就像薛定諤的那隻貓,外層不調用它,永遠不知道它是否好用。
不過爲了之後好維護,不給他人留坑,仍是儘可能寫的工整一些。

裝飾模式

裝飾模式跟靜態代理很像。

每次一說裝飾模式,就想成decorator,實際上叫wrapper更直觀些。既然是wrapper,那就得有源的句柄,在構造wrapper時得把source做爲參數傳進來。wrapper了source,一樣還wrapper其餘功能。

代理模式,Proxy Delegate,實際上Delegate也不知道本身被代理了,Proxy假裝成Delegate來執行,既然是proxy,那proxy不該該提供delegate沒有的public方法,以避免被認出來。

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

 
retrofit02.png

你能夠將ExecutorCallbackCall看成是Wrapper,而真正去執行請求的源Source是OkHttpCall。之因此要有個Wrapper類,是但願在源Source操做時去作一些額外操做。這裏的操做就是線程轉換,將子線程切換到主線程上去。

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

動態代理

再來講動態代理。以往的動態代理和靜態代理使用的場景是相似的。都想在delegate調用方法先後作一些操做。若是個人代理類有不少方法,那我得額外寫不少代碼,因此這時候就引入了動態代理。經過動態設置delegate,能夠處理不一樣代理的不一樣方法。看不懂不要緊,直接上代碼:

 
retrofit03.png

簡而言之,動態代理就是攔截調用的那個方法,在方法先後來作一些操做。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方法,先判斷有沒初始化,而後再返回。

 
retrofit04.png

差很少就是這樣,沒有Proxy的概念,只是在編譯時,把這些切面織入進去。對於pojo而言徹底是透明的。是否是很6。

不過這裏也有不少其餘的性能瓶頸,好比說我在第一次調用時,要先去數據庫fetch,這也是耗時操做。這個先跳過,有機會再跟你們八一八,我那數據庫框架是怎麼擼出來的。

適配器模式

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

若是你已經看過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

 
 
 

策略模式?

在retrofit裏,這個適配器模式不是那麼明顯。並且和其餘模式交錯在一塊兒,因此看起來很麻煩。好比這個CallAdapter又夾雜着策略模式(僅是我的見解)。

你能夠看看Rxjava裏如何去建立adapter的,它是根據api方法聲明的returnType來建立具體的CallAdapter實例的。上代碼你就明白了。

 
retrofit05.png

 
retrofit07.png

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

總結

來張提綱挈領的流程圖,沒保存的趕忙存起來。之後就能照着它本身開車了。

 
retrofit01.png

好,大概就將這麼多啦。這些就是retrofit的解耦套路了。經過一系列的設計模式,封裝思想來解耦,

看到如今,其實retrofit就是一個負責調度的controller。先給retrofit配置好,讓它可以正常工做。你給它一個方法調用,它就在內部開始運轉。這個方法之前我消化過嗎,沒消化那就用一個ServiceMethod來解析它。解析後要用來配置一個request請求。但它本身搞不定這事啊,因此須要給它一個轉接頭,經過轉接頭來使用okhttpcall。請求是作好了,可是response它又不認識,
因此又請來convertor來幫忙,轉換完畢以後才吐出一個咱們最終要的那個對象。

若是看文章不夠過癮,能夠看Stay精心錄製的視頻Retrofit分析-漂亮的解耦套路,看完你不再怕看不懂retrofit了。

並且你還能夠用Stay這種分析套路來輕鬆看懂其餘源碼。

 ====================
 

1.Retrofit分析-漂亮的解耦套路

萬萬沒想到Retrofit會這麼火,在沒看源碼以前,我簡單的認爲是由於它跟OkHttp同出一源(Square),因此纔會炒的那麼熱。又或者是由於它能支持RxJava,因此火上澆油,一發不可收拾。

後來看過Retrofit源碼以後,我才理解爲何它倍受關注,是由於它集諸優勢於一身,而且炒雞解耦。你能預見的特殊需求,都能很是容易的擴展。

沒有HTTP框架的日子

咱們先來看一下沒有HTTP框架之前,咱們是如何作請求的。

 
retrofit00.png
  1. 首先build request參數
  2. 由於不能在主線程請求HTTP,因此你得有個Executer或者線程
  3. enqueue後,經過線程去run你的請求
  4. 獲得服務器數據後,callback回調給你的上層。

大概是以上4大步驟,在沒有框架的年代,想要作一次請求,是萬分痛苦的,你須要本身管理線程切換,須要本身解析讀取數據,解析數據成對象,切換回主線程,回調給上層。

這段空白的時間持續了好久。從我10年工做起到12年,由於寫煩了重複的代碼,因此就得想辦法,把那些變化的地方封裝起來,也只是簡單的封裝。

好在官方出了AsyncTask,雖然坑不少,但若是再本身維護一個隊列,基本不會出現問題。

更好的地方是數據格式從xml變成json了。gson解放了雙手,不再用解析dom了。

早些時期的HTTP框架

後來慢慢出了很多真正的HTTP框架。Stay也借鑑了不少文章,封裝了一套適用於自身業務需求的框架。

這個時期的框架有個特色,就是拼了命去支持全部類型。比方說Volley支持直接返回Bitmap。xUtils不只大而全,並且連多線程下載也要支持。

在資源匱乏的時代,它們的存在有它們的道理。但若是說如今還用Volley作圖片請求,還在用xUtils或Afinal裏的各個模塊。那就說不過去了。

術業有專攻,百家爭鳴的時期,難道不應選擇最好的那一個嗎?(Stay沒真的用過xUtils和Afinal這種組合框架,潛意識告訴我,它們有毒,一旦某個環節出問題或者須要擴展,那代價就太大了)

Retrofit

tips:本文以retrofit最新版本2.0.1爲例,你們也能夠去github下源碼,找tag爲'parent-2.0.1'就能夠。目前代碼變更比較大。2.0.1已經使用okhttp3了,而我項目中2.0.0-beta2仍是okhttp2.5。

retrofit的最大特色就是解耦,要解耦就須要大量的設計模式,

先來看一張Stay畫的精簡流程圖(若有錯誤,請斧正),類圖就不畫了。

 
retrofit01.png

Stay在一些設計模式很明確的地方作了標記。

外觀模式,動態代理,策略模式,觀察者模式。固然還有Builder模式,工廠等這些簡單的我就沒標。

先簡述下流程吧:

  1. 經過門面Retrofit來build一個Service Interface的proxy

     
    retrofit03.png

     

  2.  當你調用這個Service Interface中的某個請求方法,會被proxy攔截。

     
    retrofit02.png
  3. 經過ServiceMethod來解析invoke的那個方法 ,經過解析註解,傳參,將它們封裝成咱們所熟悉的request。而後經過具體的返回值類型,讓以前配置的工廠生成具體的CallAdapterResponseConverter,這倆咱們稍後再解釋。

  4. new一個OkHttpCall,這個OkHttpCall算是OkHttp的包裝類,用它跟OkHttp對接,全部OkHttp須要的參數均可以看這個類。固然也仍是能夠擴展一個新的Call的,好比HttpUrlConnectionCall。可是有點耦合。看下圖標註:


     
    retrofit031.png

    紅框中顯式的指明瞭OkHttpCall,而不是經過工廠來生成Call。因此若是你不想改源碼,從新編譯,那你就只能使用OkHttp了。不過這不礙事。(可能也是由於還在持續更新中,因此這塊可能後面會改進的)

  5. 生成的CallAdapter有四個工廠,分別對應不一樣的平臺,RxJava, Java8, Guava還有一個Retrofit默認的。這個CallAdapter不太好用中文解釋。                      簡單來講就是用來將Call轉成T的一個策略。由於這裏具體請求是耗時操做,因此你須要CallAdapter去管理線程。怎麼管理,繼續往下看。

  6. 好比RxJava會根據調用方法的返回值,如Response<'T> |Result<'T>|Observable<'T> ,生成不一樣的CallAdapter。實際上就是對RxJava的回調方式作封裝。好比將response再拆解爲success和error等。(這塊仍是須要在瞭解RxJava的基礎上去理解,之後有時間能夠再詳細作分析)

  7. 在步驟5中,咱們說CallAdapter還管理線程。比方說RxJava,咱們知道,它最大的優勢能夠指定方法在什麼線程下執行。如圖

     
    retrofit04.png

    咱們在子線程訂閱(subscribeOn),在主線程觀察(observeOn)。具體它是如何作的呢。咱們看下源碼。
     
    retrofit05.png

    在adapt Call時,subscribeOn了,因此就切換到子線程中了。
  8. 在adapt Call中,具體的調用了Call execute(),execute()是同步的,enqueue()是異步的。由於RxJava已經切換了線程,因此這裏用同步方法execute()。

     
    retrofit06.png

     

  9. 接下來的具體請求,就是OkHttp的事情了,retrofit要作成的就是等待返回值。在步驟4中,咱們說OkHttpCall是OkHttp的包裝類,因此將OkHttp的response轉換成咱們要的T,也是在OkHttpCall中執行的。

  10. 固然具體的解析轉換操做也不是OkHttpCall來作的,由於它也不知道數據格式是什麼樣的。因此它只是將response包裝成retrofit標準下的response。

  11. Converter->ResponseConverter,很明顯,它是數據轉換器。它將response轉換成咱們具體想要的T。Retrofit提供了不少converter factory。好比Gson,Jackson,xml,protobuff等等。你須要什麼,就配置什麼工廠。在Service方法上聲明泛型具體類型就能夠了。

  12. 最後,經過聲明的observeOn線程回調給上層。這樣上層就拿到了最終結果。至於結果再如何處理,那就是上層的事了。

再來回顧下Stay畫的流程圖:

 
retrofit01.png

這真是漫長的旅行,Stay也是debug一個個單步調試才梳理出來的流程。固然其中還有不少巧妙的解耦方式,我這裏就不贅述了。你們能夠看看源碼分析下,當真是設計模式的經典示例。

我想如今你們應該對retrofit有所瞭解了。當你再給別人介紹retrofit的時候,就別隻說它的註解方式多新穎,多炫技了。註解式框架有不少的,像j2ee中一大把。因此註解算不得多精湛的技藝。   最牛逼的仍是它的解耦方式,這個套路沒有多年的實際架構經驗是設計不出來的。

相關文章
相關標籤/搜索