作者:@荒井。
所分析的源代碼基于Retrofit 2.3.0版本。
一般我在剛接觸一個(gè)庫(kù)時(shí),會(huì)先大致了解一下它解決什么問(wèn)題,使用了什么技術(shù)等等,這之后我會(huì)動(dòng)手寫(xiě)一個(gè)小例子并運(yùn)行起來(lái)看看效果,那么首先我來(lái)寫(xiě)一個(gè)例子。
根據(jù)官網(wǎng)描述,使用Retrofit2時(shí),是把HTTP請(qǐng)求寫(xiě)成Java接口形式,例如:
public interface ArticleService {
@GET("users/{user}/articles")
Call<ResponseBody> listArticles(@Path("user") String user);
}
然后,構(gòu)建一個(gè)Retrofit對(duì)象,并使用它生成ArticleService接口的實(shí)現(xiàn),例如:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("article.sample.com")
.build();
ArticleService service = retrofit.create(ArticleService.class);
接下來(lái),使用生成的"service"對(duì)象,調(diào)用其中的方法得到一個(gè)Call<ResponseBody>類型對(duì)象"articles",代碼如下:
Call<ResponseBody> articles = service.listArticles("arai");
最后我將這個(gè)請(qǐng)求加入隊(duì)列,并等待返回結(jié)果回調(diào):
articles.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
//成功。
}
@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
//失敗。
}
});
如果請(qǐng)求發(fā)送成功,即可在onResponse回調(diào)方法中拿到返回的數(shù)據(jù),如果失敗則會(huì)回調(diào)onFailure方法。這樣就是一個(gè)比較完整和基本的Retrofit2使用例子了。為了更好地熟悉和使用Retrofit2這個(gè)庫(kù),我準(zhǔn)備稍微深入地分析其源代碼,那么這篇文章打算從Retrofit.create(Class<T> service)方法開(kāi)始,一步一步揭開(kāi)其神秘的面紗。由于這個(gè)方法的篇幅不算長(zhǎng),先大致看一下它的全貌:
public <T> T create(final Class<T> service) {
/* 代碼段 1 */
Utils.validateServiceInterface(service);
/* 代碼段 2 */
if (validateEagerly) {
eagerlyValidateMethods(service);
}
/* 代碼段 3 */
return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service }, new InvocationHandler() {
private final Platform platform = Platform.get();
@Override
public Object invoke(Object proxy, Method method, @Nullable Object[] args) throws Throwable {
// If the method is a method from Object then defer to normal invocation.
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
if (platform.isDefaultMethod(method)) {
return platform.invokeDefaultMethod(method, service, proxy, args);
}
ServiceMethod<Object, Object> serviceMethod = (ServiceMethod<Object, Object>) loadServiceMethod(method);
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
}
});
}
我把整個(gè)方法分為3個(gè)代碼段,分為用代碼段1、2、3注釋。
代碼段1非常簡(jiǎn)單,看名字就知道,驗(yàn)證service是否是一個(gè)interface,除此之外,如果點(diǎn)進(jìn)方法查看,會(huì)發(fā)現(xiàn)它還限制了service不可再繼承其它interface,否則也會(huì)拋出異常。
代碼段2,根據(jù)validateEagerly的值決定是否進(jìn)入if,這個(gè)validateEagerly是Retrofit類的一個(gè)布爾常量,還記得如何得到Retrofit類的實(shí)例嗎,沒(méi)錯(cuò),用的是Retrofit的內(nèi)部類Builder的build()方法來(lái)構(gòu)造的,此處不貼代碼,直接給出結(jié)論,validateEagerly的值為默認(rèn)值false,這里不會(huì)進(jìn)入if條件內(nèi)執(zhí)行。
那么怎樣讓其執(zhí)行if內(nèi)代碼,它又做了什么呢?為了一探究竟,我在構(gòu)建Retrofit對(duì)象時(shí)設(shè)validateEagerly的值為true:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("article.sample.com")
.validateEagerly(true)
.build();
然后看看這個(gè)if括號(hào)里面的eagerlyValidateMethods(Class<?> service)方法:
private void eagerlyValidateMethods(Class<?> service) {
Platform platform = Platform.get();
for (Method method : service.getDeclaredMethods()) {
if (!platform.isDefaultMethod(method)) {
loadServiceMethod(method);
}
}
}
這個(gè)方法首先會(huì)獲取當(dāng)前的平臺(tái),Retrofit2中定義了2種特殊平臺(tái):Android和Java8,顯然我所在的是Android平臺(tái)。接下來(lái)遍歷service中聲明的方法,在這里發(fā)現(xiàn)Android.isDefaultMethod(Method method)是直接返回false,也就是會(huì)進(jìn)入if塊內(nèi),并對(duì)每一個(gè)method調(diào)用loadServiceMethod(Method method)方法。
代碼段2說(shuō)到這里,讓我們先短暫地跳出來(lái),回顧一下。使用Retrofit2時(shí),開(kāi)發(fā)者定義好代表網(wǎng)絡(luò)請(qǐng)求的interface,然后使用Retrofit.create()方法來(lái)獲取實(shí)現(xiàn)這個(gè)interface的對(duì)象,這里會(huì)遇到比如分析注解的操作,比如之前的例子中的listArticles方法就有注解@GET("users/{user}/articles"),這個(gè)過(guò)程是稍微慢一些的,Retrofit2為interface中的每一個(gè)method創(chuàng)建一個(gè)對(duì)應(yīng)的ServiceMethod對(duì)象,保存這個(gè)過(guò)程中的一些信息,以便后面復(fù)用。那么根據(jù)validateEagerly這個(gè)名字,好像是要盡早執(zhí)行這個(gè)操作的意思,此處暫時(shí)先記著有這樣一回事,后面會(huì)搞明白的。
/* 代碼段 3 */
return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service }, new InvocationHandler() {
private final Platform platform = Platform.get();
@Override
public Object invoke(Object proxy, Method method, @Nullable Object[] args) throws Throwable {
// If the method is a method from Object then defer to normal invocation.
/* 代碼段 3.1 */
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
/* 代碼段 3.2 */
if (platform.isDefaultMethod(method)) {
return platform.invokeDefaultMethod(method, service, proxy, args);
}
/* 代碼段 3.3 */
ServiceMethod<Object, Object> serviceMethod = (ServiceMethod<Object, Object>) loadServiceMethod(method);
/* 代碼段 3.4 */
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
/* 代碼段 3.5 */
return serviceMethod.callAdapter.adapt(okHttpCall);
}
});
接下來(lái)看代碼段3,這里用到了Java的動(dòng)態(tài)代理,直接返回動(dòng)態(tài)代理對(duì)象,根據(jù)Retrofit.create(final Class<T> service)方法的定義,它返回的是一個(gè)實(shí)現(xiàn)了Service接口的對(duì)象。現(xiàn)在我們重點(diǎn)關(guān)注Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)方法的第三個(gè)參數(shù),當(dāng)我在實(shí)際調(diào)用返回對(duì)象中的方法時(shí),會(huì)觸發(fā)這個(gè)InvocationHandler對(duì)象"h"中的invoke(Object proxy, Method method, Object[] args)方法,在最開(kāi)始的Retrofit使用例子中已經(jīng)得知,調(diào)用返回的是Call<ResponseBody>類的對(duì)象,那么我分析一下調(diào)用過(guò)程,為了方便分析,我又把它分成若干個(gè)注釋標(biāo)記的代碼段。
代碼段3.1非常簡(jiǎn)單,判斷方法存在于用戶定義的interface中還是Object類中,定義在Object類中的方法是不需要代理的,所以這里判斷false才會(huì)往下面執(zhí)行,如果是true則直接調(diào)用。
代碼段3.2的判斷方法在之前的代碼段2處已經(jīng)分析過(guò),判斷條件會(huì)返回false,這里會(huì)直接跳過(guò)。那么如果沒(méi)有跳過(guò),會(huì)調(diào)用直接調(diào)用interface中的default方法,這是在Java8中才加入的特性,即假如我在之前的例子ArticleService中加入一個(gè)default方法的話,會(huì)直接傳入?yún)?shù)并執(zhí)行這個(gè)方法。
代碼段3.3,在這里果然又遇到了loadServiceMethod(Method method)方法,這正是在分析代碼段2時(shí)沒(méi)有分析完的那個(gè)點(diǎn)。之前我說(shuō)過(guò),Retrofit2會(huì)為interface中的每一個(gè)method建立一個(gè)ServiceMethod<?, ?>對(duì)象,并存儲(chǔ)起來(lái)。這個(gè)對(duì)象中包含了對(duì)method的分析結(jié)果,比如它的返回值、它含有的注解等。loadServiceMethod(Method method)方法的邏輯是先從Retrofit.serviceMethodCache中拿取這個(gè)方法所對(duì)應(yīng)的ServiceMethod對(duì)象,如果拿到就直接返回,拿不到則構(gòu)建這個(gè)對(duì)象,并存儲(chǔ)到Retrofit.serviceMethodCache中。
ServiceMethod對(duì)象的構(gòu)建過(guò)程還是稍微有些復(fù)雜的,它會(huì)分析method的全部注解、返回值的合法性等,并保證所定義的method能提供一個(gè)網(wǎng)絡(luò)請(qǐng)求所必須的信息,同時(shí)還會(huì)獲取這個(gè)method的Converter和CallAdapter,這兩個(gè)概念在Retrofit2中很重要,在最開(kāi)始的例子中,我所定義的interface中的method,返回的類型是Call<ResponseBody>,ResponseBody即代表HTTP請(qǐng)求所返回的Body,通過(guò)解析它得到我們要的數(shù)據(jù)。為了減輕開(kāi)發(fā)者的負(fù)擔(dān),Retrofit2提供了CallAdapter和Converter這兩個(gè)概念,可以將Call和ResponseBody轉(zhuǎn)為其它類型,這讓我們十分受益,例如使用Retrofit2+Rxjava開(kāi)發(fā)時(shí),我們可以將Service中的方法像下面那樣定義,就可以只關(guān)注拿到數(shù)據(jù)后怎么展示,而不用去解析ResponseBody,并且可以直接寫(xiě)出Rxjava式的代碼。
public interface ArticleService {
@GET("users/{user}/articles")
Observable<ArticleData> listArticles(@Path("user") String user);
}
解釋完loadServiceMethod(Method method)方法都做了什么,回到分析代碼段2時(shí)留下的問(wèn)題。是否設(shè)置validateEagerly的區(qū)別,在于設(shè)置validateEagerly為true的話,ServiceMethod對(duì)象會(huì)在Retrofit.create()時(shí)就構(gòu)建,而默認(rèn)情況false,會(huì)在第一次調(diào)用方法時(shí)構(gòu)建,至于哪個(gè)好,我猜了一下,可能未必對(duì),我覺(jué)得還要視情況而定,如果一個(gè)interface中的method是在APP運(yùn)行過(guò)程中必然要執(zhí)行的方法,可能提早構(gòu)建好一些,如果并不是APP運(yùn)行期間必須要調(diào)用的方法,那可能默認(rèn)false好一些。我又特意查找了網(wǎng)上對(duì)這一塊有描述的一些文章,覺(jué)得有一種說(shuō)法比較靠譜,即設(shè)validateEagerly為true會(huì)使method定義的正確性在調(diào)用Retrofit.create()就得到驗(yàn)證,這樣方便開(kāi)發(fā)時(shí)進(jìn)行測(cè)試。
代碼段3.4,通過(guò)上一步驟得到的serviceMethod對(duì)象和傳入invoke()方法的參數(shù),建立okHttpCall對(duì)象,OkHttpCall<T>類實(shí)現(xiàn)了Call<T>接口,這個(gè)類主要的作用就是得到它之后,便可以將其加入請(qǐng)求隊(duì)列,發(fā)送網(wǎng)絡(luò)請(qǐng)求。
代碼段3.5,剛才代碼段3.3分析中已經(jīng)說(shuō)過(guò),CallAdapter的作用是將Call<T>轉(zhuǎn)換為其它類型,那么這里由于我沒(méi)有任何設(shè)置,源碼會(huì)使用默認(rèn)的CallAdapter,不進(jìn)行任何轉(zhuǎn)換,所得到的還是Call<T>類型,并直接返回。
那么通過(guò)Retrofit.create()方法得到一個(gè)實(shí)現(xiàn)interface的動(dòng)態(tài)代理對(duì)象的大致過(guò)程到這里就寫(xiě)完了,休息一下。