Retrofit是squareup公司的開源力作吠裆,和同屬squareup公司開源的OkHttp伐谈,一個(gè)負(fù)責(zé)網(wǎng)絡(luò)調(diào)度烂完,一個(gè)負(fù)責(zé)網(wǎng)絡(luò)執(zhí)行,為Android開發(fā)者提供了即方便又高效的網(wǎng)絡(luò)訪問框架诵棵。
不過抠蚣,對(duì)于Retrofit這樣設(shè)計(jì)精妙、代碼簡(jiǎn)潔履澳、使用方便的優(yōu)秀開源項(xiàng)目嘶窄,不能僅知道如何擴(kuò)展和使用,或者僅研究它采用的技術(shù)或模式奇昙,“技”當(dāng)然重要护侮,但不能忽視了背后的“道”。
對(duì)于Retrofit储耐,我們還應(yīng)該看到的羊初,是她在優(yōu)化App架構(gòu)方面的努力,以及她在提升開發(fā)效率方面的借鑒和啟示什湘。
本文試圖通過一個(gè)具體場(chǎng)景长赞,先總結(jié)Retrofit在架構(gòu)中起到的作用,再分析其實(shí)現(xiàn)原理闽撤,最后探討Retrofit給我們帶來的啟示得哆。
我們先通過一個(gè)簡(jiǎn)單的應(yīng)用場(chǎng)景來回顧Retrofit的使用過程。
基本場(chǎng)景
通常來說哟旗,使用Retrofit要經(jīng)過這樣幾個(gè)步驟
- 引用
在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'
如果需要使用更多擴(kuò)展功能贩据,比如gson轉(zhuǎn)換,rxjava適配等闸餐,可以視自己需要繼續(xù)添加引用
compile 'com.squareup.retrofit2:converter-gson:2.3.0'
compile 'com.squareup.retrofit2:adapter-rxjava2:2.3.0'
如果現(xiàn)有的擴(kuò)展包不能滿足需要饱亮,還可以自己擴(kuò)展converter,adapter等舍沙。
- 定義接口
Retrofit要求定義一個(gè)網(wǎng)絡(luò)請(qǐng)求的接口近上,接口函數(shù)里要定義url路徑、請(qǐng)求參數(shù)拂铡、返回類型壹无。
public interface INetApiService {
@GET("/demobiz/api.php")
Call<BizEntity> getBizInfo(@Query("id") String id);
}
在這個(gè)接口定義中,用注解@GET("/demobiz/api.php")聲明了url路徑感帅,用注解@Query("id") 聲明了請(qǐng)求參數(shù)斗锭。
最重要的是,用Call<BizEntity>聲明了返回值是一個(gè)Retrofit的Call對(duì)象留瞳,并且聲明了這個(gè)對(duì)象處理的數(shù)據(jù)類型為BizEntity拒迅,BizEntity是我們自定義的數(shù)據(jù)模型。
- 依次獲得Retrofit對(duì)象、接口實(shí)例對(duì)象璧微、網(wǎng)絡(luò)工作對(duì)象
首先作箍,需要新建一個(gè)retrofit對(duì)象。
然后前硫,根據(jù)上一步的接口胞得,實(shí)現(xiàn)一個(gè)retrofit加工過的接口對(duì)象。
最后屹电,調(diào)用接口函數(shù)阶剑,得到一個(gè)可以執(zhí)行網(wǎng)絡(luò)訪問的網(wǎng)絡(luò)工作對(duì)象。
//新建一個(gè)Retrofit對(duì)象
Retrofit retrofit=new Retrofit.Builder()
.baseUrl(Config.DOMAIN)//要訪問的網(wǎng)絡(luò)地址域名危号,如http://www.zhihu.com
.addConverterFactory(GsonConverterFactory.create())
.build();
...
//用retrofit加工出對(duì)應(yīng)的接口實(shí)例對(duì)象
INetApiService netApiService= retrofit.create(INetApiService.class);
//可以繼續(xù)加工出其他接口實(shí)例對(duì)象
IOtherService otherService= retrofit.create(IOtherService.class);
···
//調(diào)用接口函數(shù)牧愁,獲得網(wǎng)絡(luò)工作對(duì)象
Call<BizEntity> callWorker= netApiService.getBizInfo("id001");
這個(gè)復(fù)雜的過程下來,最終得到的callWorker對(duì)象外莲,才可以執(zhí)行網(wǎng)絡(luò)訪問猪半。
- 訪問網(wǎng)絡(luò)數(shù)據(jù)
用上一步獲取的worker對(duì)象,執(zhí)行網(wǎng)絡(luò)請(qǐng)求
callWorker.enqueue(new Callback<BizEntity>() {
@Override
public void onResponse(Call<BizEntity> call, Response<BizEntity> response) {...}
@Override
public void onFailure(Call<BizEntity> call, Throwable t) {...}
});
在回調(diào)函數(shù)里偷线,取得我們需要的BizEntity數(shù)據(jù)對(duì)象磨确。
網(wǎng)絡(luò)訪問結(jié)束。
角色與作用
我們從上面的應(yīng)用場(chǎng)景可以看出声邦,Retrofit并不做網(wǎng)絡(luò)請(qǐng)求乏奥,只是生成一個(gè)能做網(wǎng)絡(luò)請(qǐng)求的對(duì)象。
Retrofit的作用是按照接口去定制Call網(wǎng)絡(luò)工作對(duì)象
什么意思亥曹?就是說:
Retrofit不直接做網(wǎng)絡(luò)請(qǐng)求
Retrofit不直接做網(wǎng)絡(luò)請(qǐng)求
Retrofit不直接做網(wǎng)絡(luò)請(qǐng)求
重要的事情說三遍邓了。
網(wǎng)絡(luò)請(qǐng)求的目標(biāo)雖然是數(shù)據(jù),但是我們需要為這個(gè)數(shù)據(jù)寫大量的配套代碼媳瞪,發(fā)起請(qǐng)求的對(duì)象Call驶悟,接收數(shù)據(jù)的對(duì)象CallBack,做數(shù)據(jù)轉(zhuǎn)換的對(duì)象Converter材失,以及檢查和處理異常的對(duì)象等。
這對(duì)于一個(gè)項(xiàng)目的開發(fā)硫豆、擴(kuò)展和維護(hù)來說龙巨,都是成本和風(fēng)險(xiǎn)。
而Retrofit做的事情熊响,就是為開發(fā)者節(jié)省這部分的工作量旨别,Retrofit一方面從底層統(tǒng)一用OkHttp去做網(wǎng)絡(luò)處理;另一方面在外層靈活提供能直接融入業(yè)務(wù)邏輯的Call網(wǎng)絡(luò)訪問對(duì)象汗茄。
具體來說秸弛,Retrofit只負(fù)責(zé)生產(chǎn)對(duì)象,生產(chǎn)能做網(wǎng)絡(luò)請(qǐng)求的工作對(duì)象,他有點(diǎn)像一個(gè)工廠递览,只提供產(chǎn)品叼屠,工廠本身不處理網(wǎng)絡(luò)請(qǐng)求,產(chǎn)品才能處理網(wǎng)絡(luò)請(qǐng)求绞铃。
Retrofit在網(wǎng)絡(luò)請(qǐng)求中的作用大概可以這樣理解:
我們看到镜雨,從一開始,Retrofit要提供的就是個(gè)Call工作對(duì)象儿捧。
換句話說荚坞,對(duì)于給Retrofit提供的那個(gè)接口
public interface INetApiService {
@GET("/demobiz/api.php")
Call<BizEntity> getBizInfo(@Query("id") String id);
}
這個(gè)接口并不是傳統(tǒng)意義上的網(wǎng)絡(luò)請(qǐng)求接口,這個(gè)接口不是用來獲取數(shù)據(jù)的接口菲盾,而是用來生產(chǎn)對(duì)象的接口颓影,這個(gè)接口相當(dāng)于一個(gè)工廠,接口中每個(gè)函數(shù)的返回值不是網(wǎng)絡(luò)數(shù)據(jù)懒鉴,而是一個(gè)能進(jìn)行網(wǎng)絡(luò)請(qǐng)求的工作對(duì)象诡挂,我們要先調(diào)用函數(shù)獲得工作對(duì)象,再用這個(gè)工作對(duì)象去請(qǐng)求網(wǎng)絡(luò)數(shù)據(jù)疗我。
所以Retrofit的實(shí)用價(jià)值意義在于咆畏,他能根據(jù)你的接口定義,靈活地生成對(duì)應(yīng)的網(wǎng)絡(luò)工作對(duì)象吴裤,然后你再擇機(jī)去調(diào)用這個(gè)對(duì)象訪問網(wǎng)絡(luò)旧找。
理解了這一點(diǎn),我們才能去擴(kuò)展Retrofit麦牺,并理解Retrofit的設(shè)計(jì)思想钮蛛。
功能擴(kuò)展
我們先來看Retrofit能擴(kuò)展哪些功能,然后再去理解Retrofit的工作原理剖膳。
Retrofit主要可以擴(kuò)展三個(gè)地方:
- OkHttpClient
Retrofit使用OkHttpClient來實(shí)現(xiàn)網(wǎng)絡(luò)請(qǐng)求魏颓,這個(gè)OkHttpClient雖然不能替換為其他的網(wǎng)絡(luò)執(zhí)行框架比如Volley,但是Retrofit允許我們使用自己擴(kuò)展OkHttpClient吱晒,一般最常擴(kuò)展的就是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
擴(kuò)展的是對(duì)返回的數(shù)據(jù)類型的自動(dòng)轉(zhuǎn)換甸饱,把一種數(shù)據(jù)對(duì)象轉(zhuǎn)換為另一種數(shù)據(jù)對(duì)象。
在上述場(chǎng)景中仑濒,GsonConverterFactory可以把Http訪問得到的json字符串轉(zhuǎn)換為Java數(shù)據(jù)對(duì)象BizEntity叹话,這個(gè)BizEntity是在INetApiService接口中要求的的。
這種轉(zhuǎn)換我們自己也經(jīng)常做墩瞳,很好理解驼壶。
如果現(xiàn)有的擴(kuò)展包不能滿足需要,可以繼承Retrofit的接口喉酌。retrofit2.Converter<F,T>热凹,自己實(shí)現(xiàn)Converter和ConverterFactory泵喘。
在創(chuàng)建Retrofit對(duì)象時(shí),可以插入我們自定義的ConverterFactory般妙。
//retrofit對(duì)象
Retrofit retrofit=new Retrofit.Builder()
.baseUrl(Config.DOMAIN)
.addConverterFactory(GsonConverterFactory.create())
.addConverterFactory(YourConverterFactory.create())//添加自定義Converter
.build();
- addCallAdapterFactory
擴(kuò)展的是對(duì)網(wǎng)絡(luò)工作對(duì)象callWorker的自動(dòng)轉(zhuǎn)換纪铺,把Retrofit中執(zhí)行網(wǎng)絡(luò)請(qǐng)求的Call對(duì)象,轉(zhuǎn)換為接口中定義的Call對(duì)象股冗。
這個(gè)轉(zhuǎn)換不太好理解霹陡,我們可以對(duì)照下圖來理解:
Retrofit本身用一個(gè)OkHttpCall的類負(fù)責(zé)處理網(wǎng)絡(luò)請(qǐng)求,而我們?cè)诮涌谥卸x需要定義很多種Call止状,例如Call<BizEntity>烹棉,或者Flowable<BizEntity>等,接口里的Call和Retrofit里的OkHttpCall并不一致怯疤,所以我們需要用一個(gè)CallAdapter去做一個(gè)適配轉(zhuǎn)換浆洗。
(Retrofit底層雖然使用了OkHttpClient去處理網(wǎng)絡(luò)請(qǐng)求,但她并沒有使用okhttp3.call這個(gè)Call接口集峦,而是自己又建了一個(gè)retrofit2.Call接口伏社,OkHttpCall繼承的是retrofit2.Call,與okhttp3.call只是引用關(guān)系塔淤。
這樣的設(shè)計(jì)符合依賴倒置原則摘昌,可以盡可能的與OkHttpClient解耦。)
這其實(shí)是Retrofit非常核心高蜂,也非常好用的一個(gè)設(shè)計(jì)聪黎,如果我們?cè)诮涌谥幸蟮暮瘮?shù)返回值是個(gè)RxJava的Flowable對(duì)象
public interface INetApiService {
@GET("/demobiz/api.php")
Flowable<BizEntity> getBizInfo(@Query("id") String id);
}
那么我們只需要為Retrofit添加對(duì)應(yīng)的擴(kuò)展
//retrofit對(duì)象
Retrofit retrofit=new Retrofit.Builder()
.baseUrl(Config.DOMAIN)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
就能得到Flowable類型的callWorker對(duì)象
//用retrofit加工出對(duì)應(yīng)的接口實(shí)例對(duì)象
INetApiService netApiService= retrofit.create(INetApiService.class);
···
//調(diào)用接口函數(shù),獲得網(wǎng)絡(luò)工作對(duì)象
Flowable<BizEntity> callWorker= netApiService.getBizInfo("id001");
在這里备恤,callAdapter做的事情就是把retrofit2.Call對(duì)象適配轉(zhuǎn)換為Flowable<T>對(duì)象稿饰。
同樣,如果現(xiàn)有的擴(kuò)展包不能滿足需要露泊,可以繼承Retrofit的接口retrofit2.CallAdapter<R,T>喉镰,自己實(shí)現(xiàn)CallAdapter和CallAdapterFactory。
Retrofit實(shí)現(xiàn)原理
Retrofit固然設(shè)計(jì)精妙惭笑,代碼簡(jiǎn)潔侣姆,使用方便,但相應(yīng)的沉噩,我們要理解Retrofit的實(shí)現(xiàn)原理也不太容易铺敌,這么精妙的設(shè)計(jì)是極佳的研究素材,我們不能僅僅停留在知道怎么使用屁擅,怎么擴(kuò)展的階段,那實(shí)在是對(duì)這個(gè)優(yōu)秀開源項(xiàng)目的浪費(fèi)产弹。
其實(shí)派歌,Retrofit使用的弯囊,就是動(dòng)態(tài)代理,方法注解胶果、建造者和適配器等成熟的技術(shù)或模式匾嘱,但是由于她的設(shè)計(jì)緊湊,而且動(dòng)態(tài)代理屏蔽了很多過程上的細(xì)節(jié)早抠,所以比較難以理解霎烙。
Retrofit實(shí)現(xiàn)原理——從動(dòng)態(tài)代理開始
從前面的使用場(chǎng)景可知,retrofit會(huì)生成一個(gè)接口實(shí)例蕊连。
//用retrofit加工出對(duì)應(yīng)的接口實(shí)例對(duì)象
INetApiService netApiService= retrofit.create(INetApiService.class);
到Retrofit源碼里看create函數(shù)悬垃,是一個(gè)動(dòng)態(tài)代理。
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);
}
});
}
要理解動(dòng)態(tài)代理甘苍,最好要看到動(dòng)態(tài)生成的代理類尝蠕。
由于動(dòng)態(tài)代理是在運(yùn)行時(shí)動(dòng)態(tài)生成的代理類,用常規(guī)的反編譯方法無法查看载庭,一般要使用Java提供的sun.misc.ProxyGenerator.generateProxyClass(String proxyName,class[] interfaces)函數(shù)生成代理類看彼,函數(shù)會(huì)返回byte[]字節(jié)碼,然后對(duì)字節(jié)碼反編譯得到Java代碼囚聚。
有一個(gè)小問題是靖榕,AndroidStudio并不提供sun.misc這個(gè)包,我們需要用IntelliJ或者Eclipse建立一個(gè)Java工程顽铸,在Java環(huán)境里調(diào)用這個(gè)函數(shù)茁计。
拿到的代理類,大概是這樣的:
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",//反射函數(shù)
new Class[] { Class.forName("java.lang.String") });//反射參數(shù)
//接口中定義的其他方法
...
}
...
}
//返回接口實(shí)例對(duì)象
public INetApiService (InvocationHandler invocationHandler){
super(invocationHandler);
}
//
public final Call getBizInfo(String str){
...
try{//用Handler去調(diào)用
return (Call)this.h.invoke(this, m3, new Object[]{str});
}
}
}
我們可以看到跋破,代理類生成的是一個(gè)INetApiService接口的實(shí)例對(duì)象簸淀,該對(duì)象的getBizInfo函數(shù)返回的是接口中定義的Call網(wǎng)絡(luò)工作對(duì)象,這也體現(xiàn)了Retrofit的核心價(jià)值毒返,生成接口定義的Call網(wǎng)絡(luò)工作對(duì)象租幕。
那么,這個(gè)Call網(wǎng)絡(luò)工作對(duì)象是如何生成的呢拧簸,上面動(dòng)態(tài)代理生成的代碼是這樣的:
return (Call)this.h.invoke(this, m3, new Object[]{str});
也就是說劲绪,這個(gè)Call網(wǎng)絡(luò)工作對(duì)象是在InvocationHandler中實(shí)現(xiàn)的,也就是在Retrofit.create函數(shù)中盆赤,由InvocationHandler實(shí)現(xiàn)的贾富。
這樣我們就明白了,Retrofit使用動(dòng)態(tài)代理牺六,其實(shí)是為了開發(fā)者在寫代碼時(shí)方便調(diào)用颤枪,而真正負(fù)責(zé)生產(chǎn)Call網(wǎng)絡(luò)工作對(duì)象的,還是Retrofit.create函數(shù)中定義的這個(gè)InvocationHandler淑际,這個(gè)InvocationHandler的代碼我們?cè)儋N一遍:
new InvocationHandler() {
...
ServiceMethod<Object, Object> serviceMethod =
(ServiceMethod<Object, Object>) loadServiceMethod(method);
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
}
ServiceMethod能讓我們準(zhǔn)確解析到INetApiService中定義的函數(shù)畏纲,為最后的適配轉(zhuǎn)換提供轉(zhuǎn)換目標(biāo)扇住,詳細(xì)分析我們后面再說,先看適配轉(zhuǎn)換的過程盗胀。
我們看到艘蹋,Retrofit內(nèi)部默認(rèn)使用OkHttpCall對(duì)象去處理網(wǎng)絡(luò)請(qǐng)求,但是返回的網(wǎng)絡(luò)工作對(duì)象是經(jīng)過適配器轉(zhuǎn)換的票灰,轉(zhuǎn)換成接口定義的那種Call網(wǎng)絡(luò)工作對(duì)象女阀。
這個(gè)適配轉(zhuǎn)換,就是Retrofit能按照接口去定制Call網(wǎng)絡(luò)工作對(duì)象的秘密屑迂。
Retrofit實(shí)現(xiàn)原理——適配轉(zhuǎn)換Call對(duì)象
我們?cè)诔跏蓟疪etrofit對(duì)象時(shí)浸策,好像不添加CallAdapterFactory也能實(shí)現(xiàn)適配轉(zhuǎn)換。
//retrofit對(duì)象
Retrofit retrofit=new Retrofit.Builder()
.baseUrl(Config.DOMAIN)
.addConverterFactory(GsonConverterFactory.create())
//可以不添加CallAdapterFactory
.build();
這是怎么回事呢屈糊,我們知道Retrofit使用了建造者模式的榛,建造者模式的特定就是實(shí)現(xiàn)了建造和使用的分離,所以建造者模式的建造函數(shù)里逻锐,一般會(huì)有很復(fù)雜的對(duì)象創(chuàng)建和初始化過程夫晌,所以我們要看一下Retrofit的build函數(shù)。
public Retrofit build() {
...
okhttp3.Call.Factory callFactory = this.callFactory;
if (callFactory == null) {
callFactory = new OkHttpClient();//使用OkHttpClient處理網(wǎng)絡(luò)請(qǐng)求
}
...
//根據(jù)當(dāng)前運(yùn)行平臺(tái)昧诱,設(shè)置默認(rèn)的callAdapterFactory
adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));
...
return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
callbackExecutor, validateEagerly);
}
這段代碼里晓淀,我們看到Retrofit使用OkHttpClient處理網(wǎng)絡(luò)請(qǐng)求,并且會(huì)添加默認(rèn)的callAdapterFactory盏档,這個(gè)platform是一個(gè)簡(jiǎn)單工廠凶掰,能根據(jù)當(dāng)前系統(tǒng)平臺(tái)去生成對(duì)應(yīng)的callAdapterFactory
private static Platform findPlatform() {
try {
Class.forName("android.os.Build");
if (Build.VERSION.SDK_INT != 0) {
return new Android();//根據(jù)當(dāng)前系統(tǒng)平臺(tái)返回相應(yīng)的對(duì)象
}
...
}
...
static class Android extends Platform {
...
@Override CallAdapter.Factory defaultCallAdapterFactory(@Nullable Executor callbackExecutor) {
if (callbackExecutor == null) throw new AssertionError();
return new ExecutorCallAdapterFactory(callbackExecutor);
}
...
}
這個(gè)Platform是Retrofit在Builder的構(gòu)造函數(shù)里初始化的。
所以蜈亩,在Retrofit.build()函數(shù)中懦窘,我們?yōu)镽etrofit默認(rèn)添加的callAdapterFactory,是在Platform中為Android系統(tǒng)設(shè)定的ExecutorCallAdapterFactory稚配。
我們看ExecutorCallAdapterFactory的代碼畅涂,這是一個(gè)工廠類,可以返回CallAdapter對(duì)象:
@Override
public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
...
return new CallAdapter<Object, Call<?>>() {
...
// 轉(zhuǎn)換后 轉(zhuǎn)換前道川,也就是OkHttpCall
@Override public Call<Object> adapt(Call<Object> call) {
return new ExecutorCallbackCall<>(callbackExecutor, call);
}
};
}
在adapt函數(shù)中午衰,適配器會(huì)把Retrofit中用來訪問網(wǎng)絡(luò)的OkHttpCall,轉(zhuǎn)換為一個(gè)ExecutorCallbackCall(繼承了INetApiService接口里要求返回的網(wǎng)絡(luò)工作對(duì)象retrofit2.Call),
這個(gè)例子里面,由于OkHttpCall和ExecutorCallbackCall都實(shí)現(xiàn)了retrofit2.Call接口红伦,結(jié)果出現(xiàn)了從Call<Object>轉(zhuǎn)換為Call<Object>的情況,這可能不容易理解帅戒,我們換個(gè)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中
// 轉(zhuǎn)換后 轉(zhuǎn)換前,也就是OkHttpCall
@Override public Object adapt(Call<R> call) {
...
Observable<?> observable;
...
return observable;
}
這個(gè)CallAdapter的轉(zhuǎn)換就比較明顯了崖技,把retrofit2.Call對(duì)象通過適配器轉(zhuǎn)換為了一個(gè)實(shí)為Observable<?>的Object對(duì)象逻住。
至此施流,我們可以理解Retrofit根據(jù)接口定義動(dòng)態(tài)生產(chǎn)Call網(wǎng)絡(luò)請(qǐng)求工作對(duì)象的原理了,其實(shí)就是通過適配器把retrofit2.Call對(duì)象轉(zhuǎn)換為目標(biāo)對(duì)象鄙信。
至于適配器轉(zhuǎn)換過程中,如何實(shí)現(xiàn)的對(duì)象轉(zhuǎn)換忿晕,就可以根據(jù)需求來自由實(shí)現(xiàn)了装诡,比如利用靜態(tài)代理等,如有必要践盼,我們可以自行開發(fā)擴(kuò)展鸦采,Retrofit框架并不限制我們對(duì)于適配器的實(shí)現(xiàn)方式。
Retrofit實(shí)現(xiàn)原理——函數(shù)解析咕幻、網(wǎng)絡(luò)請(qǐng)求和數(shù)據(jù)轉(zhuǎn)換
在前面分析中渔伯,我們知道了Retrofit的整體工作流程,就是Retrofit用動(dòng)態(tài)代理生成Call網(wǎng)絡(luò)請(qǐng)求對(duì)象肄程,在這個(gè)過程中锣吼,用適配器把Retrofit底層的retrofit2.Call對(duì)象轉(zhuǎn)換為INetApiService中定義的Call網(wǎng)絡(luò)請(qǐng)求對(duì)象(如Flowable)。
問題是蓝厌,Retrofit具體是如何知道了INetApiService中定義的Call網(wǎng)絡(luò)請(qǐng)求對(duì)象玄叠,如何實(shí)現(xiàn)網(wǎng)絡(luò)請(qǐng)求,以及如何執(zhí)行的數(shù)據(jù)轉(zhuǎn)換呢拓提?
具體過程如下读恃;
首先,根據(jù)INetApiService中定義的函數(shù)代态,解析函數(shù)寺惫,得到函數(shù)的具體定義,并生成對(duì)應(yīng)的ServiceMethod蹦疑。
然后西雀,根據(jù)這個(gè)ServiceMethod,實(shí)現(xiàn)一個(gè)OkHttpCall的Call對(duì)象必尼,負(fù)責(zé)在Retrofit底層實(shí)現(xiàn)網(wǎng)絡(luò)訪問蒋搜。
其中,在網(wǎng)絡(luò)訪問返回了網(wǎng)絡(luò)數(shù)據(jù)時(shí)判莉,根據(jù)ServiceMethod實(shí)現(xiàn)數(shù)據(jù)轉(zhuǎn)換豆挽。
最后,利用上一小節(jié)中匹配的適配器券盅,把OkHttpCall對(duì)象轉(zhuǎn)換為INetApiService要求的Call網(wǎng)絡(luò)請(qǐng)求對(duì)象帮哈。
所以,我們要了解的就是函數(shù)解析锰镀、網(wǎng)絡(luò)請(qǐng)求和數(shù)據(jù)轉(zhuǎn)換這三個(gè)動(dòng)作娘侍,至于最后的適配轉(zhuǎn)換咖刃,在上一節(jié)中已經(jīng)分析過了。
1. 函數(shù)解析
在接口函數(shù)里憾筏,用注解描述了輸入?yún)?shù)嚎杨,用Java對(duì)象定義了返回值類型,所以對(duì)輸入?yún)?shù)和返回值氧腰,ServiceMethod采取了不同的方式去處理枫浙。
輸入?yún)?shù)
輸入?yún)?shù)是用來描述url的,它的處理相對(duì)簡(jiǎn)單古拴,ServiceMethod會(huì)根據(jù)反射得到的Method箩帚,取得Annotation注解信息,這些注解是Retrofit自己預(yù)定義好的(retrofit2.http.*)黄痪,ServiceMethod根據(jù)預(yù)先的定義紧帕,直接判斷注解所屬的邏輯分支,在有網(wǎng)絡(luò)請(qǐng)求時(shí)分情況進(jìn)行處理桅打,就能得到目標(biāo)url是嗜,http請(qǐng)求頭等數(shù)據(jù)。
返回值
返回值是需要用CallAdapter去適配的油额,所以核心在于生成對(duì)應(yīng)的CallAdapter叠纷。
在Retrofit生成Call網(wǎng)絡(luò)工作對(duì)象時(shí),她通過動(dòng)態(tài)代理獲取到了接口函數(shù)的Method定義潦嘶,從這個(gè)Method中可以獲取函數(shù)定義的返回對(duì)象類型涩嚣,由于這個(gè)轉(zhuǎn)換是需要CallAdapterFactory生產(chǎn)CallAdapter對(duì)象去實(shí)現(xiàn),而Retrofit事先并不知道要使用哪個(gè)Factory掂僵,所以她是遍歷所有的CallAdapterFactory航厚,根據(jù)目標(biāo)函數(shù)的返回值類型,讓每個(gè)Factory都去嘗試生產(chǎn)一個(gè)CallAdapter锰蓬,哪個(gè)成功就用哪個(gè)幔睬。
2. 網(wǎng)絡(luò)請(qǐng)求
OkHttpCall繼承的retrofit2.Call接口是為了依賴倒置解耦的,真正的網(wǎng)絡(luò)請(qǐng)求是由OkHttpCall內(nèi)部引用的okhttp3.call處理的芹扭,這個(gè)okhttp3.call是
借道ServiceMethod獲取的Retrofit中的callFactory麻顶,也就是Retrofit中的OkHttpClient。
整個(gè)引用鏈條是這樣的:
OkHttpCall--okhttp3.call
-->
ServiceMethod--callFactory
-->
Retrofit.build()--callFactory//(如未擴(kuò)展賦值)new OkHttpClient();
-->
Retrofit.Builder().client(mClient)//(可能有擴(kuò)展賦值)擴(kuò)展過的OkHttpClient
最終的網(wǎng)絡(luò)請(qǐng)求是由OkHttpCall調(diào)用OkHttpClient發(fā)出的舱卡,調(diào)用和回調(diào)等過程辅肾,也就是在OkHttpCall中處理的。
網(wǎng)絡(luò)請(qǐng)求的生成過程中轮锥,為了使用接口函數(shù)中定義的參數(shù)矫钓,OkHttpCall會(huì)調(diào)用ServiceMethod來生成Request請(qǐng)求對(duì)象,再交給OkHttpCall去處理。
3. 數(shù)據(jù)轉(zhuǎn)換
因?yàn)榛卣{(diào)是在OkHttpCall中處理的新娜,所以對(duì)回調(diào)數(shù)據(jù)的轉(zhuǎn)換也在OkHttpCall中觸發(fā)赵辕,為了符合接口函數(shù)中定義的返回?cái)?shù)據(jù)類型,OkHttpCall會(huì)調(diào)用ServiceMethod來轉(zhuǎn)換Response返回?cái)?shù)據(jù)對(duì)象概龄。
OkHttpCall對(duì)返回的網(wǎng)絡(luò)數(shù)據(jù)还惠,會(huì)調(diào)用一個(gè)serviceMethod.toResponse(ResponseBody body)函數(shù),函數(shù)中執(zhí)行的是:
R toResponse(ResponseBody body) throws IOException {
return responseConverter.convert(body);
}
這個(gè)函數(shù)可以把原始的okhttp3. ResponseBody數(shù)據(jù)轉(zhuǎn)換為INetApiService接口中要求的數(shù)據(jù)類型(如BizEntity類型)私杜。
從代碼可以看出吸重,實(shí)現(xiàn)數(shù)據(jù)轉(zhuǎn)換的核心對(duì)象其實(shí)是responseConverter,這個(gè)Converter實(shí)際上要依次經(jīng)過Retrofit的建造和ServiceMethod的建造后歪今,才能確定下來的。
Retrofit建造時(shí)添加數(shù)據(jù)轉(zhuǎn)換工廠
Retrofit里有converterFactries列表颜矿,這是在我們初始化Retrofit實(shí)例時(shí)添加的
//retrofit對(duì)象
Retrofit retrofit=new Retrofit.Builder()
.baseUrl(Config.DOMAIN)
.addConverterFactory(GsonConverterFactory.create())
.addConverterFactory(YourConverterFactory.create())//添加自定義Converter
.build();
ServiceMethod建造時(shí)設(shè)定數(shù)據(jù)轉(zhuǎn)換器
ServiceMethod在建造時(shí)寄猩,就已經(jīng)確定了對(duì)應(yīng)的是INetApiService中的哪個(gè)函數(shù),所以需要明確設(shè)定自己的Converter<R,T>轉(zhuǎn)換對(duì)象
public ServiceMethod build() {
...
responseConverter = createResponseConverter();
...
}
這需要調(diào)用Retrofit
private Converter<ResponseBody, T> createResponseConverter() {
...
retrofit.responseBodyConverter(responseType, annotations);
}
Retrofit會(huì)在自己的轉(zhuǎn)換器工廠列表中遍歷每個(gè)ConverterFactory骑疆,嘗試根據(jù)ServiceMethod所對(duì)應(yīng)的目標(biāo)數(shù)據(jù)類型田篇,找到Converter數(shù)據(jù)轉(zhuǎn)換類
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轉(zhuǎn)換為例,GsonConverterFactory會(huì)通過getAdapter來嘗試匹配目標(biāo)數(shù)據(jù)類型:
public <T> TypeAdapter<T> getAdapter(TypeToken<T> type) {...}
如果可以匹配箍铭,那么前面調(diào)用serviceMethod.toResponse(ResponseBody body)函數(shù)時(shí)泊柬,會(huì)調(diào)用
R toResponse(ResponseBody body) throws IOException {
return responseConverter.convert(body);
}
在調(diào)用這段代碼時(shí),其實(shí)就是調(diào)用了Gson中最終執(zhí)行數(shù)據(jù)轉(zhuǎn)換的代碼:
@Override public T convert(ResponseBody value) throws IOException {
JsonReader jsonReader = gson.newJsonReader(value.charStream());
try {
return adapter.read(jsonReader);
} finally {
value.close();
}
}
總結(jié)來說诈火,Retrofit在類的單一職責(zé)方面分隔的很好兽赁,OkHttpCall類只負(fù)責(zé)網(wǎng)絡(luò)交互,凡是需要知道函數(shù)定義的冷守,都交給ServiceMethod類去處理刀崖,而ServiceMethod類對(duì)使用者不公開,因?yàn)镽etrofit是個(gè)外觀模式拍摇,而所有需要擴(kuò)展的都在Retrofit的建造者中實(shí)現(xiàn)亮钦,他們的分工大概是這樣的:
這三個(gè)類分工合作,共同實(shí)現(xiàn)了函數(shù)解析充活、網(wǎng)絡(luò)訪問和數(shù)據(jù)轉(zhuǎn)換蜂莉,并保留了良好的可擴(kuò)展性。
Retrofit實(shí)現(xiàn)原理——整體結(jié)構(gòu)與分工實(shí)現(xiàn)
至此混卵,Retrofit的實(shí)現(xiàn)細(xì)節(jié)就已經(jīng)基本清楚了映穗,他用動(dòng)態(tài)代理去定制接口定義的Call網(wǎng)絡(luò)工作對(duì)象,用適配器去把底層的Call對(duì)象轉(zhuǎn)換為目標(biāo)Call對(duì)象淮菠,用函數(shù)解析/OkHttpClient/數(shù)據(jù)轉(zhuǎn)換等實(shí)現(xiàn)對(duì)Call對(duì)象的適配轉(zhuǎn)換男公,并能處理真正的網(wǎng)絡(luò)請(qǐng)求。
這里面涉及的整體結(jié)構(gòu)和角色分工,大概可以這樣表示:
其中枢赔,擴(kuò)展適配器澄阳、擴(kuò)展數(shù)據(jù)轉(zhuǎn)換和擴(kuò)展OkHttpClient,雖然都是通過Retrofit實(shí)現(xiàn)擴(kuò)展踏拜,但真正的使用者是Retrofit內(nèi)部的ServiceMethod碎赢、OkHttpCall和okhttp3.call等類或?qū)ο蟆?/p>
反推Retrofit的設(shè)計(jì)過程
如果我們不直接正面分析Retrofit的結(jié)構(gòu)設(shè)計(jì)和技術(shù)細(xì)節(jié),而是先從Retrofit的功能和作用入手速梗,倒過來推測(cè)Retrofit的目標(biāo)肮塞,進(jìn)而分析其架構(gòu)和搭建細(xì)節(jié),Retrofit為什么會(huì)設(shè)計(jì)成這樣就很好理解了姻锁。
Retrofit的功能是按照接口定義枕赵,自動(dòng)定制Call網(wǎng)絡(luò)工作對(duì)象,所以Retrofit的目標(biāo)應(yīng)該就是避免為網(wǎng)絡(luò)訪問開發(fā)大量的配套代碼位隶。
為了實(shí)現(xiàn)這一目標(biāo)拷窜,Retrofit需要分析哪些是易變的,哪些是不變的涧黄,然后分別處理篮昧。
由于Retrofit提供網(wǎng)絡(luò)訪問的工作對(duì)象,又是服務(wù)于具體業(yè)務(wù)笋妥,所以可以分網(wǎng)絡(luò)訪問和具體業(yè)務(wù)兩部分來分析懊昨。
網(wǎng)絡(luò)訪問的不變性
對(duì)于網(wǎng)絡(luò)訪問來說,不變的是一定有一個(gè)實(shí)現(xiàn)網(wǎng)絡(luò)訪問的對(duì)象春宣,Retrofit選用了自家的OkHttpClient酵颁,不過為了把Retrofit和OkHttp兩個(gè)項(xiàng)目解耦合,Retrofit根據(jù)依賴倒置原則月帝,定義了Retrofit自己的Call即retrofit2.call材义,并定義了操作網(wǎng)絡(luò)請(qǐng)求的OkHttpCall。
網(wǎng)絡(luò)訪問的易變性
對(duì)于網(wǎng)絡(luò)訪問來說嫁赏,易變的是網(wǎng)絡(luò)訪問的url其掂、請(qǐng)求方式(get/post等)、Http請(qǐng)求的Header設(shè)置與安全設(shè)置等潦蝇,以及返回的數(shù)據(jù)類型款熬。
針對(duì)易變的url和請(qǐng)求方式,Retrofit使用了方法注解的方式攘乒,可讀性良好贤牛,擴(kuò)展性優(yōu)異,但這需要實(shí)現(xiàn)對(duì)接口函數(shù)中注解的解析则酝,這樣就有了ServiceMethod殉簸。
針對(duì)Http請(qǐng)求的各種設(shè)置,其實(shí)Retrofit沒做什么,因?yàn)镽etrofit使用的OkHttp有攔截器機(jī)制般卑,可以應(yīng)付這種變化武鲁。
針對(duì)返回的數(shù)據(jù)類型,由于目標(biāo)數(shù)據(jù)類型與業(yè)務(wù)有關(guān)蝠检,是不確定的沐鼠,Retrofit無法提供一個(gè)萬能的轉(zhuǎn)換類,所以Retrofit提供了擴(kuò)展接口叹谁,允許開發(fā)者自己定義ConverterFactory和Converter饲梭,去實(shí)現(xiàn)潛在的數(shù)據(jù)類型轉(zhuǎn)換。
具體業(yè)務(wù)的不變性
對(duì)于具體業(yè)務(wù)來說焰檩,不變的是一定要有一個(gè)Call網(wǎng)絡(luò)工作對(duì)象憔涉,所以Retrofit可以有一個(gè)生產(chǎn)對(duì)象的機(jī)制(像工廠一樣)
具體業(yè)務(wù)的易變性
對(duì)于具體業(yè)務(wù)來說,易變的就是這個(gè)Call網(wǎng)絡(luò)工作對(duì)象的類型析苫,不僅有CallBacl回調(diào)监氢、可能還有Flowable工作流、或者其他潛在的對(duì)象類型藤违。
針對(duì)這種Call對(duì)象的易變性,Retrofit也是無法提供一個(gè)萬能的實(shí)現(xiàn)類纵揍,所以也是提供了擴(kuò)展解耦顿乒,允許開發(fā)者自己定義CallAdapterFactory和CallAdapter,去實(shí)現(xiàn)潛在的Call類型轉(zhuǎn)換泽谨。
因?yàn)檫@種Call對(duì)象的生產(chǎn)需要有大量的配套代碼璧榄,為了簡(jiǎn)化代碼,Retrofit使用動(dòng)態(tài)代理來生產(chǎn)這個(gè)對(duì)象吧雹。
最后骨杂,因?yàn)樾枰幚淼姆椒ê蛯?duì)象太多太復(fù)雜,需要使用建造者模式來把建造過程和使用過程分離開雄卷。
這樣倒著走一遍之后搓蚪,我們?cè)倏碦etrofit的設(shè)計(jì)和實(shí)現(xiàn)原理,就會(huì)覺得水到渠成丁鹉,對(duì)于Retrofit精妙的設(shè)計(jì)更會(huì)有一種切身體會(huì)妒潭。
借鑒與啟示
在上文的反推過程中,我們可窺見(瞎猜)Jake大神的一些思路:
- 萬物皆對(duì)象
網(wǎng)絡(luò)訪問后揣钦,回調(diào)數(shù)據(jù)是個(gè)對(duì)象雳灾;網(wǎng)絡(luò)訪問本身也是個(gè)對(duì)象。 - 依賴倒置
哪怕是使用自家的OkHttp冯凹,哪怕底層調(diào)用的始終是OkHttpClient谎亩,也需要依賴一個(gè)抽象的retrofit2.Call接口,依賴于抽象,而不是依賴于具體匈庭。 - 單一職責(zé)
類的職責(zé)需要維持單一夫凸,流程需要但是超出自己職責(zé)的功能,去調(diào)用相關(guān)的類實(shí)現(xiàn)嚎花,比如OkHttpClient和ServiceMethod的各自職責(zé)與調(diào)用關(guān)系寸痢。 - 迪米特法則
內(nèi)部實(shí)現(xiàn)再復(fù)雜,對(duì)于外部調(diào)用者也只展示他需要的那些功能紊选,例如Retrofit啼止。 - 自動(dòng)>人工
動(dòng)態(tài)代理的使用,可以用自動(dòng)生成的模板代碼兵罢,減輕人工編寫配套代碼的工作量献烦,成本更低,風(fēng)險(xiǎn)更低卖词。 - 利用工廠類開放擴(kuò)展
對(duì)于流程確定巩那,但方法不能確定的,利用工廠類此蜈,對(duì)調(diào)用者開放擴(kuò)展能力即横。 - 利用多個(gè)工廠類組成擴(kuò)展列表
如果1個(gè)工廠類不能實(shí)現(xiàn)兼得,何不設(shè)置一個(gè)工廠類列表裆赵,在多個(gè)工廠類中东囚,看哪個(gè)工廠類能解決問題。 - 利用建造者模式把建造和使用分離
這樣使用者不需要關(guān)系復(fù)雜的建造過程战授,例如Retrofit和ServiceMethod页藻。 - 利用外觀模式減少對(duì)復(fù)雜子系統(tǒng)的操作
雖然有復(fù)雜的子系統(tǒng)協(xié)同工作,調(diào)用者只需要調(diào)用最外層的Retrofit即可植兰。 - 其他
開放封閉份帐、接口隔離、里式替換楣导、靜態(tài)代理等設(shè)計(jì)原則或設(shè)計(jì)模式都有體現(xiàn)也都很熟悉了废境,就不再啰嗦。
最后感嘆一下筒繁。
對(duì)于網(wǎng)絡(luò)訪問的抽象與優(yōu)化彬坏,實(shí)際上是個(gè)非常難的課題,在Retrofit之前膝晾,大家努力的方向基本上都是Volley/OkHttp這種圍繞底層網(wǎng)絡(luò)訪問的工作栓始。
因?yàn)樵降讓拥臇|西越容易抽象,越上升到接近業(yè)務(wù)層血当,就越容易在紛擾的業(yè)務(wù)層中迷失幻赚。
Retrofit能精準(zhǔn)地抓到Call網(wǎng)絡(luò)工作對(duì)象這個(gè)關(guān)鍵點(diǎn)禀忆,并能通過一系列精巧的設(shè)計(jì)實(shí)現(xiàn)對(duì)這種類型“飄忽不定”的對(duì)象的自動(dòng)化定制生產(chǎn),著實(shí)令人贊嘆落恼。
參考
Retrofit
你真的會(huì)用Retrofit2嗎?Retrofit2完全教程
Retrofit2 源碼解析
Retrofit 框架源碼學(xué)習(xí)
拆輪子系列:拆 Retrofit
Android 動(dòng)態(tài)代理以及利用動(dòng)態(tài)代理實(shí)現(xiàn) ServiceHook