[Android] Retrofit 源碼分析之 ServiceMethod 對(duì)象

前言

大家好,我又來(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í)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容