前言
大家好,我又來(lái)學(xué)習(xí) Retrofit 了,可能這是最后一篇關(guān)于 Retrofit 框架的文章了。我發(fā)現(xiàn)源碼分析這回事,當(dāng)時(shí)看明白了,過(guò)些時(shí)候再看就想這寫的啥玩意。所以大家還是多看多學(xué)多分析。
另外跟我自己文章結(jié)構(gòu)組織也有很大關(guān)系,我盡量在以后加強(qiáng)這點(diǎn),做到簡(jiǎn)潔清晰有層次。

這周我們要分析一下 ServiceMethod。
ServiceMethod 是什么
ServiceMethod 會(huì)將你的接口方法調(diào)用轉(zhuǎn)化為一個(gè) Call 對(duì)象。也就說(shuō)對(duì)于每一個(gè)接口方法,他都會(huì)創(chuàng)建一個(gè)與之對(duì)應(yīng)的 ServiceMethod,實(shí)際上上篇文章也有提到。
那么 ServiceMethod 是從哪開(kāi)始的呢?
MyApi api = retrofit.create(MyApi.class);
這句代碼總沒(méi)有忘記吧?
create 方法根據(jù)你的『Http 接口』返回了一個(gè)『動(dòng)態(tài)代理』,當(dāng)我們調(diào)用接口方法時(shí),會(huì)轉(zhuǎn)發(fā)到『動(dòng)態(tài)代理』的 invoke 方法。內(nèi)有三句重點(diǎn):
ServiceMethod serviceMethod = loadServiceMethod(method);
OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
最后一句就不說(shuō)了,把你的 OkHttpCall 轉(zhuǎn)化適配成指定類型的 Call 對(duì)象。我們主要分析其余兩句。
loadServiceMethod(method)
流程概要
實(shí)際上,這個(gè)方法我們?cè)谏瞎?jié)簡(jiǎn)單學(xué)習(xí)過(guò)了:
ServiceMethod loadServiceMethod(Method method) {
ServiceMethod result;
synchronized (serviceMethodCache) {
result = serviceMethodCache.get(method);
if (result == null) {
result = new ServiceMethod.Builder(this, method).build();
serviceMethodCache.put(method, result);
}
}
return result;
}
當(dāng)接口方法調(diào)用時(shí),會(huì)先從緩存(一個(gè) Map)中找,如果沒(méi)有的話就新建一個(gè),我們看到 ServiceMethod 也是用到建造者模式:
result = new ServiceMethod.Builder(this, method).build();
傳入了 retrofit 對(duì)象和我們調(diào)用的方法 method 作為參數(shù)創(chuàng)建了一個(gè) ServiceMethod:
public Builder(Retrofit retrofit, Method method) {
this.retrofit = retrofit;
this.method = method;
// 獲取 method 中的所有注解
this.methodAnnotations = method.getAnnotations();
// 獲取 method 中方法的參數(shù)類型
this.parameterTypes = method.getGenericParameterTypes();
// 獲得參數(shù)的值
this.parameterAnnotationsArray = method.getParameterAnnotations();
}
build() 方法
然后調(diào)用 build() 方法。
build() 方法內(nèi)容比較多,主要是一些判斷并根據(jù) retrofit 對(duì)象的實(shí)例變量狀態(tài)創(chuàng)建 ServiceMethod 的變量。關(guān)鍵是一下幾行代碼:
// 根據(jù) retrofit 對(duì)象的 CallAdapterFactory 為 ServiceMethod 創(chuàng)建一個(gè) callAdapter
callAdapter = createCallAdapter();
// 根據(jù) retrofit 對(duì)象創(chuàng)建一個(gè) responseConverter,默認(rèn)是一個(gè) BuildInConveter
responseConverter = createResponseConverter();
// 解析 method 的所有注解
for (Annotation annotation : methodAnnotations) {
parseMethodAnnotation(annotation);
}
前兩句代碼都很好理解,這里講一下 parseMethodAnnotation 方法,該方法內(nèi)容是許多個(gè)判斷語(yǔ)句,這里我們看其中一個(gè)判斷:
if (annotation instanceof DELETE) {
parseHttpMethodAndPath("DELETE", ((DELETE) annotation).value(), false);
} else if (annotation instanceof GET) {
parseHttpMethodAndPath("GET", ((GET) annotation).value(), false);
} else if ...
可以看出就是對(duì)注解的類型進(jìn)行判斷,如果是 DELETE、POST、PATCH 等這樣的 Http 請(qǐng)求方式,則調(diào)用 parseHttpMethodAndPath 設(shè)置 ServiceMethod 實(shí)例的 httpMethod 變量。
例如我們的接口是這樣的:
@GET("/users/{user}")
Call<TestModel> repo(@Path("user") String user);
那么當(dāng)我們調(diào)用 repo 方法時(shí),會(huì)將 GET 設(shè)置到 httpMethod 中,將其值 "/users/{user}" 進(jìn)一步解析,設(shè)置到 relativeUrl 中。
那么 repo(@Path("user") String user) 里的參數(shù)呢?我們之前創(chuàng)建 method 時(shí)獲取了方法的『參數(shù)類型』和『參數(shù)值』,所以在解析過(guò)外層的想 @GET,@Headers 這樣的注解后,就會(huì)進(jìn)行方法參數(shù)注解的解析:
parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);
具體邏輯就不多說(shuō)了,這里通過(guò) Java 的 『反射機(jī)制』和『自定義注解』特性,使得我們能夠通過(guò)一種簡(jiǎn)單的方式,把我們編寫在 Http 接口的信息,都轉(zhuǎn)化為了 ServiceMethod 所必要的變量值。
OkHttpCall
現(xiàn)在 ServiceMethod 創(chuàng)建好了,我們學(xué)習(xí)到他包含了許多東西,包括了我們要請(qǐng)求的地址,請(qǐng)求的方式以及 CallAdapter、Converter 等等。
現(xiàn)在我們要用他來(lái)構(gòu)造一個(gè) okHttpCall:
OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
當(dāng)你調(diào)用 okHttpCall 的 enqueue 方法或 execute 方法時(shí),如果還沒(méi)生成一個(gè) okhttp3.Call 對(duì)象則會(huì)調(diào)用以下方法:
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;
}
這里創(chuàng)建了調(diào)用了 ServiceMethod.toRequest(args) 方法構(gòu)建了一個(gè) HTTP Request 對(duì)象:
RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl, headers,
contentType, hasBody, isFormEncoded, isMultipart);
可以看到就是把我們編寫的信息作為參數(shù)去構(gòu)建的。
這里的 Request 對(duì)象和 Call 對(duì)象實(shí)際上都屬于 okHttp 的范疇了,也就是說(shuō)至此我們的『Http 接口』最終轉(zhuǎn)化為了一個(gè)『okhttp3.Call』并將工作交給了 okHttp 去執(zhí)行。
那么 Retrofit 框架的工作也就大體完成了。
結(jié)語(yǔ)
實(shí)際上這節(jié)的內(nèi)容并不復(fù)雜,只是細(xì)節(jié)的東西太多了,有許多 Retrofit 框架以外的內(nèi)容,我自己也并不熟悉。所以其實(shí)內(nèi)容比較雜亂,但是 Retrofit 的主要思想就是利用 Java 的特性,來(lái)減少了編寫代碼時(shí)的工作量。
這確實(shí)是一個(gè)框架設(shè)計(jì)的經(jīng)典作品,值得大家去深入學(xué)習(xí)。