前言
Retrofit A type-safe HTTP client for Android and Java
Retrofit,是一個(gè)基于http請(qǐng)求庫(kù)二次封裝的HTTP客戶端,將 REST API 轉(zhuǎn)換為 Java 接口脯厨。
基于注解逞力,進(jìn)一步解放了生產(chǎn)力,使得http請(qǐng)求就像調(diào)用方法一樣簡(jiǎn)單厘肮,如絲般順滑转砖。
結(jié)構(gòu)概覽
項(xiàng)目結(jié)構(gòu)整體分四個(gè)部分恢筝,Builder -> Proxy -> Invocation -> RawCall
這里我們把基于Retrofit的HTTP通信比做是郵遞信件。
郵遞信件
- 信封:當(dāng)我們準(zhǔn)備好信件之后料睛,要在信封上寫(xiě)郵寄地址丐箩,收件人,可能還要備注勿折(是的恤煞,我暴露了我的年齡屎勘,如今很多人可能都沒(méi)有過(guò)寫(xiě)信寄信的體驗(yàn))。
- 郵遞員:然后我們親自去送信嗎居扒?No概漱,我們把信投入郵箱,交給郵遞員代為送信就行了苔货。
- 郵局:然后郵遞員會(huì)根據(jù)信封上的信息對(duì)信件進(jìn)行分揀犀概,寄信或收信均經(jīng)由郵局統(tǒng)一處理
- 郵寄方式:最后就是交給運(yùn)送單位送信了立哑,空運(yùn)或是陸運(yùn)等。
基于Retrofit的HTTP通信
- Builder:當(dāng)我們準(zhǔn)備好數(shù)據(jù)之后姻灶,要指定服務(wù)端的通信地址铛绰,處理接口地址,請(qǐng)求方法产喉,可能還要備注是否有body捂掰、是否是multipart。
- Proxy:然后通信的事交給代理去做曾沈,代理會(huì)幫你做好一系列的工作这嚣,比如注解解析,Call適配塞俱,以及請(qǐng)求調(diào)度等
- Invocation:這里負(fù)責(zé)調(diào)度同步或異步請(qǐng)求姐帚,請(qǐng)求裝配和響應(yīng)解析
- RawCall:這里就是具體的通信工具了,可選Okhttp等框架來(lái)做具體的Http通信障涯。
來(lái)看看寄信和Retrofit之間的對(duì)比:
大概過(guò)程就是這樣罐旗,郵遞員會(huì)把信送出去,并在適合的時(shí)機(jī)把對(duì)方的回信取回來(lái)送給你唯蝶,當(dāng)然如果你的信件是表白情書(shū)九秀,那也很可能會(huì)收不到回信的,畢竟表白成功的概率要看人品的粘我。不要傷心鼓蜒,HTTP通信也會(huì)有時(shí)候收不到服務(wù)端的回信噢。
目錄概覽
│ BuiltInConverters.java # 內(nèi)建Converter
│ Call.java # 發(fā)送請(qǐng)求接收響應(yīng)的retrofit方法調(diào)用
│ CallAdapter.java # 適配Call的響應(yīng)類(lèi)型征字,將默認(rèn)響應(yīng)類(lèi)型R轉(zhuǎn)換為類(lèi)型T
│ Callback.java # 返回服務(wù)端或離線請(qǐng)求的響應(yīng)體
│ Converter.java # HTTP交互中都弹,轉(zhuǎn)換對(duì)象為數(shù)據(jù) 或 從數(shù)據(jù)轉(zhuǎn)換為對(duì)象
│ DefaultCallAdapterFactory.java # 默認(rèn)CallAdapter工廠
│ ExecutorCallAdapterFactory.java # http請(qǐng)求執(zhí)行器工廠
│ HttpException.java # 非2xx HTTP響應(yīng)的異常處理
│ OkHttpCall.java # 真正調(diào)用OkHttp3發(fā)送Http請(qǐng)求的類(lèi)
│ package-info.java # 包描述
│ ParameterHandler.java # 參數(shù)注解解析器
│ Platform.java # 平臺(tái)適配(Java/Android)
│ RequestBuilder.java # 請(qǐng)求拼裝
│ Response.java # 原汁原味的HTTP 響應(yīng)體,所謂 T body
│ Retrofit.java # 組裝工廠柔纵,基于建造者模式拼裝自定義HTTP交互所需的組件缔杉,并作為總調(diào)度暴露接口
│ ServiceMethod.java # 框架核心處理類(lèi)锤躁,注解解析器調(diào)度搁料,生成請(qǐng)求(包含api url、path系羞、http請(qǐng)求方法郭计、請(qǐng)
# 求頭、是否是multipart等等),并返回用于發(fā)起http請(qǐng)求的Call對(duì)象
│ Utils.java # 工具類(lèi)
│
└─http # http注解定義 (直接引用了Javadoc中的描述椒振,均為提高生產(chǎn)力的注解)
Body.java # control the request body of a POST/PUT request
DELETE.java # Make a DELETE request
Field.java # Named pair for a form-encoded request
FieldMap.java # Named key/value pairs for a form-encoded request
FormUrlEncoded.java # Denotes that the request body will use form URL encoding
GET.java # Make a GET request
HEAD.java # Make a HEAD request
Header.java # Replaces the header with the value of its target
HeaderMap.java # Adds headers specified in the Map
Headers.java # Adds headers literally supplied in the value
HTTP.java # Use a custom HTTP verb for a request
Multipart.java # Denotes that the request body is multi-part
OPTIONS.java # Make an OPTIONS request
package-info.java # Package description
Part.java # Denotes a single part of a multi-part request
PartMap.java # Denotes name and value parts of a multi-part request
PATCH.java # Make a PATCH request
Path.java # Named replacement in a URL path segment
POST.java # Make a POST request
PUT.java # Make a PUT request
Query.java # Query parameter appended to the URL
QueryMap.java # Query parameter keys and values appended to the URL
QueryName.java # Query parameter appended to the URL that has no value
Streaming.java # Treat the response body on methods returning Response as is, i.e.
# without converting body() to byte[]
Url.java # URL resolved against the base URL
Retrofit的基本用法
讓我們從基本用法開(kāi)始昭伸,先看如何使用,順著這個(gè)藤澎迎,摸摸如何實(shí)現(xiàn)的瓜庐杨。
用 Java 接口的方式定義一個(gè)HTTP API.
public interface GitHubService {
@GET("users/{user}/repos")
Call<List<Repo>> listRepos(@Path("user") String user);
}
Retrofit 類(lèi)生成一個(gè) GitHubService 接口的實(shí)現(xiàn)實(shí)例.
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.build();
GitHubService service = retrofit.create(GitHubService.class);
Each Call from the created GitHubService can make a synchronous or asynchronous HTTP request to the remote webserver.
GitHubService實(shí)例的每一個(gè)方法調(diào)用都支持同步或異步HTTP請(qǐng)求.
Call<List<Repo>> repos = service.listRepos("octocat");
執(zhí)行同步或異步HTTP請(qǐng)求选调,得到HTTP響應(yīng)數(shù)據(jù).
Response<List<Repo>> response = repos.execute();
Retrofit的源碼解析
首先我們心里要有個(gè)概念,Retrofit的核心關(guān)鍵詞:注解灵份、動(dòng)態(tài)代理仁堪、轉(zhuǎn)換器、適配器
Retrofit就是基于這四個(gè)關(guān)鍵詞搭建起來(lái)的充分解耦填渠,靈活弦聂,可插拔的優(yōu)秀框架。
下面我們結(jié)合Retrofit設(shè)計(jì)圖流程來(lái)解讀代碼氛什。 還記得流程嗎莺葫? Builder -> Proxy -> Invocation -> RawCall.
Flow - Builder
Retrofit.Builder() .baseUrl("https://api.github.com/") ... .build();
Tips.設(shè)計(jì)模式之Builder模式
基于Builder模式,裝配一系列零部件,比如base請(qǐng)求地址枪眉,gson轉(zhuǎn)換器捺檬,Rxjava適配器,HTTP請(qǐng)求client(比如裝配OKHTTP)等贸铜。
// Retrofit.java -> class Builder
public Retrofit build() {
...
return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
callbackExecutor, validateEagerly);
}
返回一個(gè)裝配了 callFactory欺冀,converterFactories,adapterFactories萨脑,callbackExecutor 和指定了 baseUrl 的 Retrofit 實(shí)例隐轩。
注:validateEagerly
,用于指定是否預(yù)先解析注解渤早,加速接口訪問(wèn)效率职车。
Flow - Proxy
GitHubService service = retrofit.create(GitHubService.class);
我們知道,Java 接口是不可以直接 new 實(shí)例的鹊杖,那么這個(gè) create 方法看起來(lái)又像是返回了一個(gè) GitHubService 接口類(lèi)型的實(shí)現(xiàn)實(shí)例悴灵,這是怎么回事呢?我們來(lái)看下 create 的實(shí)現(xiàn)骂蓖。
// Retrofit.java
public <T> T create(final Class<T> service) {
Utils.validateServiceInterface(service);
if (validateEagerly) {
eagerlyValidateMethods(service);
}
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);
}
});
}
create方法主要就一個(gè)return,返回了一個(gè)Proxy.newProxyInstance生成的動(dòng)態(tài)代理對(duì)象积瞒。原來(lái)這里是通過(guò)動(dòng)態(tài)代理的方式生成了 GitHubService 接口的代理實(shí)例,那么后續(xù) GitHubService 接口的方法都可以通過(guò)代理去調(diào)用了登下。
為什么用動(dòng)態(tài)代理茫孔?
這是Retrofit設(shè)計(jì)的核心思路,基于動(dòng)態(tài)代理被芳,可以為后續(xù)在調(diào)用 GitHubService 接口的相關(guān)方法時(shí)先攔截下來(lái)缰贝,做完一系列工作后(即注解解析,請(qǐng)求轉(zhuǎn)換畔濒,適配等)剩晴,再去完成方法本尊想要完成的工作,這就是動(dòng)態(tài)代理的魅力侵状。
Tips.動(dòng)態(tài)代理
Call<List<Repo>> repos = service.listRepos("octocat");
通過(guò)代理對(duì)象 service 調(diào)用接口方法 listRepos 赞弥,會(huì)被動(dòng)態(tài)代理攔截毅整,調(diào)用Proxy.newProxyInstance方法中的InvocationHandler對(duì)象的 invoke 方法。
invoke中主要由ServiceMethod和CallAdapter完成了三件事:
- 請(qǐng)求方法的注解解析
- 創(chuàng)建OkHttpCall實(shí)例绽左,為后續(xù)流程中的HTTP請(qǐng)求執(zhí)行做準(zhǔn)備毛嫉,詳見(jiàn) Flow - Invocation.
- 適配Call的響應(yīng)類(lèi)型,將默認(rèn)響應(yīng)類(lèi)型R轉(zhuǎn)換為類(lèi)型T
ServiceMethod<Object, Object> serviceMethod =
(ServiceMethod<Object, Object>) loadServiceMethod(method);
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
ServiceMethod.java
// ServiceMethod.java
public ServiceMethod build() {
callAdapter = createCallAdapter();
responseType = callAdapter.responseType();
...
responseConverter = createResponseConverter();
for (Annotation annotation : methodAnnotations) {
parseMethodAnnotation(annotation);
}
...
int parameterCount = parameterAnnotationsArray.length;
parameterHandlers = new ParameterHandler<?>[parameterCount];
for (int p = 0; p < parameterCount; p++) {
Type parameterType = parameterTypes[p];
...
Annotation[] parameterAnnotations = parameterAnnotationsArray[p];
...
parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);
}
...
return new ServiceMethod<>(this);
}
獲取callAdapter妇菱、responseType承粤、responseConverter接口對(duì)象
解析Method的注解
解析Method的參數(shù)注解
解析Method的參數(shù)中使用了依賴請(qǐng)求API的動(dòng)態(tài)參數(shù)的注解,交由ParameterHandler處理
CallAdapter.java
public interface CallAdapter<R, T> {
Type responseType();
...
T adapt(Call<R> call);
...
}
適配Call的響應(yīng)類(lèi)型闯团,將默認(rèn)響應(yīng)類(lèi)型R轉(zhuǎn)換為類(lèi)型T.比如官方的RxJavaCallAdapter可以結(jié)合Rxjava特性對(duì)Call的響應(yīng)做RxJava觀察者模式轉(zhuǎn)換辛臊,進(jìn)一步解放生產(chǎn)力。
注:未在Builder階段指定CallAdapter(如 RxJavaCallAdapterFactory )的情況下房交,默認(rèn)的 CallAdapter 不對(duì)Call做任何處理彻舰。
見(jiàn) DefaultCallAdapterFactory:
final class DefaultCallAdapterFactory extends CallAdapter.Factory {
static final CallAdapter.Factory INSTANCE = new DefaultCallAdapterFactory();
...
...
@Override public Call<Object> adapt(Call<Object> call) {
return call;
}
}
}
Flow - Invocation
Response<List<Repo>> response = repos.execute();
這一步開(kāi)始基于同步的方式執(zhí)行HTTP請(qǐng)求讽坏,并得到返回的HTTP響應(yīng)數(shù)據(jù).
本質(zhì)上是執(zhí)行了 OkHttpCall 的 execute方法.
// OkHttpCall.java
@Override public Response<T> execute() throws IOException {
synchronized (this) {
...
...
call = rawCall = createRawCall();
}
...
return parseResponse(call.execute());
}
如你所見(jiàn)坠陈,這里創(chuàng)建了RawCall,即真正的去執(zhí)行HTTP請(qǐng)求任務(wù)的對(duì)象坎缭。
這里還負(fù)責(zé)HTTP請(qǐng)求的響應(yīng)數(shù)據(jù)解析白群。
我們看下createRawCall()
干了什么尚胞。
// OkHttpCall.java
private okhttp3.Call createRawCall() throws IOException {
Request request = serviceMethod.toRequest(args);
okhttp3.Call call = serviceMethod.callFactory.newCall(request);
...
return call;
}
serviceMethod.toRequest()的功能:
// ServiceMethod.java
/** Builds an HTTP request from method arguments. */
Request toRequest(@Nullable Object... args) throws IOException {
RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl, headers,
contentType, hasBody, isFormEncoded, isMultipart);
...
return requestBuilder.build();
}
toRequest 方法通過(guò) RequestBuilder 創(chuàng)建了 okhttp3 做 HTTP 請(qǐng)求時(shí)需要的 Request 對(duì)象。
serviceMethod.callFactory.newCall(request)的功能:
建立一個(gè)請(qǐng)求通道帜慢,為執(zhí)行HTTP請(qǐng)求做準(zhǔn)備笼裳。
這里callFactory可以由使用者指定,默認(rèn)為 OkHttpClient粱玲,見(jiàn):
// Retrofit.java
okhttp3.Call.Factory callFactory = this.callFactory;
if (callFactory == null) {
callFactory = new OkHttpClient();
}
回頭看下 OkHttpCall 中 execute 方法最后一句: return parseResponse(call.execute());
這里調(diào)用真正的HTTP請(qǐng)求客戶端的請(qǐng)求執(zhí)行方法躬柬。也就是來(lái)到了接下來(lái)的一個(gè)流程。
Flow - RawCall
上個(gè) Flow 中最后一步抽减, call.execute()
,開(kāi)啟了真正的HTTP請(qǐng)求允青,即通過(guò) okhttp3 完成HTTP請(qǐng)求。
這個(gè)部分沒(méi)什么代碼可講卵沉,屬于面向接口開(kāi)發(fā)的典范颠锉,要講就該去講 Okhttp 框架的源碼了。
這個(gè)部分引出了 Retrofit 的開(kāi)源擁有者-Square 公司的另一個(gè)優(yōu)秀的開(kāi)源項(xiàng)目 Okhttp,是不是也很想一探究竟偎箫?