轉載地址:https://www.jianshu.com/p/f57b7cdb1c99
Retrofit是squareup公司的開源力作,和同屬squareup公司開源的OkHttp,一個負責網絡調度,一個負責網絡執行,爲Android開發者提供了即方便又高效的網絡訪問框架。
不過,對於Retrofit這樣設計精妙、代碼簡潔、使用方便的優秀開源項目,不能僅知道如何擴展和使用,或者僅研究它採用的技術或模式,“技”當然重要,但不能忽視了背後的“道”。
對於Retrofit,我們還應該看到的,是她在優化App架構方面的努力,以及她在提升開發效率方面的借鑑和啓示。
本文試圖通過一個具體場景,先總結Retrofit在架構中起到的作用,再分析其實現原理,最後探討Retrofit給我們帶來的啓示。
我們先通過一個簡單的應用場景來回顧Retrofit的使用過程。
基本場景
通常來說,使用Retrofit要經過這樣幾個步驟
- 引用
在gradle文件中引用retrofit
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等。
- 定義接口
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是我們自定義的數據模型。
- 依次獲得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對象,纔可以執行網絡訪問。
- 訪問網絡數據
用上一步獲取的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主要可以擴展三個地方:
- 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();
- 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();
- 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大神的一些思路:
- 萬物皆對象
網絡訪問後,回調數據是個對象;網絡訪問本身也是個對象。 - 依賴倒置
哪怕是使用自家的OkHttp,哪怕底層調用的始終是OkHttpClient,也需要依賴一個抽象的retrofit2.Call接口,依賴於抽象,而不是依賴於具體。 - 單一職責
類的職責需要維持單一,流程需要但是超出自己職責的功能,去調用相關的類實現,比如OkHttpClient和ServiceMethod的各自職責與調用關係。 - 迪米特法則
內部實現再複雜,對於外部調用者也只展示他需要的那些功能,例如Retrofit。 - 自動>人工
動態代理的使用,可以用自動生成的模板代碼,減輕人工編寫配套代碼的工作量,成本更低,風險更低。 - 利用工廠類開放擴展
對於流程確定,但方法不能確定的,利用工廠類,對調用者開放擴展能力。 - 利用多個工廠類組成擴展列表
如果1個工廠類不能實現兼得,何不設置一個工廠類列表,在多個工廠類中,看哪個工廠類能解決問題。 - 利用建造者模式把建造和使用分離
這樣使用者不需要關係複雜的建造過程,例如Retrofit和ServiceMethod。 - 利用外觀模式減少對複雜子系統的操作
雖然有複雜的子系統協同工作,調用者只需要調用最外層的Retrofit即可。 - 其他
開放封閉、接口隔離、裏式替換、靜態代理等設計原則或設計模式都有體現也都很熟悉了,就不再囉嗦。
最後感嘆一下。
對於網絡訪問的抽象與優化,實際上是個非常難的課題,在Retrofit之前,大家努力的方向基本上都是Volley/OkHttp這種圍繞底層網絡訪問的工作。
因爲越底層的東西越容易抽象,越上升到接近業務層,就越容易在紛擾的業務層中迷失。
Retrofit能精準地抓到Call網絡工作對象這個關鍵點,並能通過一系列精巧的設計實現對這種類型“飄忽不定”的對象的自動化定製生產,着實令人讚歎。
參考
Retrofit
你真的會用Retrofit2嗎?Retrofit2完全教程
Retrofit2 源碼解析
Retrofit 框架源碼學習
拆輪子系列:拆 Retrofit
Android 動態代理以及利用動態代理實現 ServiceHook
作者:藍灰_q
鏈接:https://www.jianshu.com/p/f57b7cdb1c99
來源:簡書
簡書著作權歸作者所有,任何形式的轉載都請聯繫作者獲得授權並註明出處。