前言
Retrofit A type-safe HTTP client for Android and Java
Retrofit,是一個基于http請求庫二次封裝的HTTP客戶端,將 REST API 轉換為 Java 接口。
基于注解,進一步解放了生產力,使得http請求就像調用方法一樣簡單,如絲般順滑。
結構概覽
項目結構整體分四個部分,Builder -> Proxy -> Invocation -> RawCall
這里我們把基于Retrofit的HTTP通信比做是郵遞信件。
郵遞信件
- 信封:當我們準備好信件之后,要在信封上寫郵寄地址,收件人,可能還要備注勿折(是的,我暴露了我的年齡,如今很多人可能都沒有過寫信寄信的體驗)。
- 郵遞員:然后我們親自去送信嗎?No,我們把信投入郵箱,交給郵遞員代為送信就行了。
- 郵局:然后郵遞員會根據信封上的信息對信件進行分揀,寄信或收信均經由郵局統一處理
- 郵寄方式:最后就是交給運送單位送信了,空運或是陸運等。
基于Retrofit的HTTP通信
- Builder:當我們準備好數據之后,要指定服務端的通信地址,處理接口地址,請求方法,可能還要備注是否有body、是否是multipart。
- Proxy:然后通信的事交給代理去做,代理會幫你做好一系列的工作,比如注解解析,Call適配,以及請求調度等
- Invocation:這里負責調度同步或異步請求,請求裝配和響應解析
- RawCall:這里就是具體的通信工具了,可選Okhttp等框架來做具體的Http通信。
來看看寄信和Retrofit之間的對比:
大概過程就是這樣,郵遞員會把信送出去,并在適合的時機把對方的回信取回來送給你,當然如果你的信件是表白情書,那也很可能會收不到回信的,畢竟表白成功的概率要看人品的。不要傷心,HTTP通信也會有時候收不到服務端的回信噢。
目錄概覽
│ BuiltInConverters.java # 內建Converter
│ Call.java # 發送請求接收響應的retrofit方法調用
│ CallAdapter.java # 適配Call的響應類型,將默認響應類型R轉換為類型T
│ Callback.java # 返回服務端或離線請求的響應體
│ Converter.java # HTTP交互中,轉換對象為數據 或 從數據轉換為對象
│ DefaultCallAdapterFactory.java # 默認CallAdapter工廠
│ ExecutorCallAdapterFactory.java # http請求執行器工廠
│ HttpException.java # 非2xx HTTP響應的異常處理
│ OkHttpCall.java # 真正調用OkHttp3發送Http請求的類
│ package-info.java # 包描述
│ ParameterHandler.java # 參數注解解析器
│ Platform.java # 平臺適配(Java/Android)
│ RequestBuilder.java # 請求拼裝
│ Response.java # 原汁原味的HTTP 響應體,所謂 T body
│ Retrofit.java # 組裝工廠,基于建造者模式拼裝自定義HTTP交互所需的組件,并作為總調度暴露接口
│ ServiceMethod.java # 框架核心處理類,注解解析器調度,生成請求(包含api url、path、http請求方法、請
# 求頭、是否是multipart等等),并返回用于發起http請求的Call對象
│ Utils.java # 工具類
│
└─http # http注解定義 (直接引用了Javadoc中的描述,均為提高生產力的注解)
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的基本用法
讓我們從基本用法開始,先看如何使用,順著這個藤,摸摸如何實現的瓜。
用 Java 接口的方式定義一個HTTP API.
public interface GitHubService {
@GET("users/{user}/repos")
Call<List<Repo>> listRepos(@Path("user") String user);
}
Retrofit 類生成一個 GitHubService 接口的實現實例.
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實例的每一個方法調用都支持同步或異步HTTP請求.
Call<List<Repo>> repos = service.listRepos("octocat");
執行同步或異步HTTP請求,得到HTTP響應數據.
Response<List<Repo>> response = repos.execute();
Retrofit的源碼解析
首先我們心里要有個概念,Retrofit的核心關鍵詞:注解、動態代理、轉換器、適配器
Retrofit就是基于這四個關鍵詞搭建起來的充分解耦,靈活,可插拔的優秀框架。
下面我們結合Retrofit設計圖流程來解讀代碼。 還記得流程嗎? Builder -> Proxy -> Invocation -> RawCall.
Flow - Builder
Retrofit.Builder() .baseUrl("https://api.github.com/") ... .build();
Tips.設計模式之Builder模式
基于Builder模式,裝配一系列零部件,比如base請求地址,gson轉換器,Rxjava適配器,HTTP請求client(比如裝配OKHTTP)等。
// Retrofit.java -> class Builder
public Retrofit build() {
...
return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
callbackExecutor, validateEagerly);
}
返回一個裝配了 callFactory,converterFactories,adapterFactories,callbackExecutor 和指定了 baseUrl 的 Retrofit 實例。
注:validateEagerly
,用于指定是否預先解析注解,加速接口訪問效率。
Flow - Proxy
GitHubService service = retrofit.create(GitHubService.class);
我們知道,Java 接口是不可以直接 new 實例的,那么這個 create 方法看起來又像是返回了一個 GitHubService 接口類型的實現實例,這是怎么回事呢?我們來看下 create 的實現。
// 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方法主要就一個return,返回了一個Proxy.newProxyInstance生成的動態代理對象。原來這里是通過動態代理的方式生成了 GitHubService 接口的代理實例,那么后續 GitHubService 接口的方法都可以通過代理去調用了。
為什么用動態代理?
這是Retrofit設計的核心思路,基于動態代理,可以為后續在調用 GitHubService 接口的相關方法時先攔截下來,做完一系列工作后(即注解解析,請求轉換,適配等),再去完成方法本尊想要完成的工作,這就是動態代理的魅力。
Tips.動態代理
Call<List<Repo>> repos = service.listRepos("octocat");
通過代理對象 service 調用接口方法 listRepos ,會被動態代理攔截,調用Proxy.newProxyInstance方法中的InvocationHandler對象的 invoke 方法。
invoke中主要由ServiceMethod和CallAdapter完成了三件事:
- 請求方法的注解解析
- 創建OkHttpCall實例,為后續流程中的HTTP請求執行做準備,詳見 Flow - Invocation.
- 適配Call的響應類型,將默認響應類型R轉換為類型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接口對象
解析Method的注解
解析Method的參數注解
解析Method的參數中使用了依賴請求API的動態參數的注解,交由ParameterHandler處理
CallAdapter.java
public interface CallAdapter<R, T> {
Type responseType();
...
T adapt(Call<R> call);
...
}
適配Call的響應類型,將默認響應類型R轉換為類型T.比如官方的RxJavaCallAdapter可以結合Rxjava特性對Call的響應做RxJava觀察者模式轉換,進一步解放生產力。
注:未在Builder階段指定CallAdapter(如 RxJavaCallAdapterFactory )的情況下,默認的 CallAdapter 不對Call做任何處理。
見 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();
這一步開始基于同步的方式執行HTTP請求,并得到返回的HTTP響應數據.
本質上是執行了 OkHttpCall 的 execute方法.
// OkHttpCall.java
@Override public Response<T> execute() throws IOException {
synchronized (this) {
...
...
call = rawCall = createRawCall();
}
...
return parseResponse(call.execute());
}
如你所見,這里創建了RawCall,即真正的去執行HTTP請求任務的對象。
這里還負責HTTP請求的響應數據解析。
我們看下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 方法通過 RequestBuilder 創建了 okhttp3 做 HTTP 請求時需要的 Request 對象。
serviceMethod.callFactory.newCall(request)的功能:
建立一個請求通道,為執行HTTP請求做準備。
這里callFactory可以由使用者指定,默認為 OkHttpClient,見:
// Retrofit.java
okhttp3.Call.Factory callFactory = this.callFactory;
if (callFactory == null) {
callFactory = new OkHttpClient();
}
回頭看下 OkHttpCall 中 execute 方法最后一句: return parseResponse(call.execute());
這里調用真正的HTTP請求客戶端的請求執行方法。也就是來到了接下來的一個流程。
Flow - RawCall
上個 Flow 中最后一步, call.execute()
,開啟了真正的HTTP請求,即通過 okhttp3 完成HTTP請求。
這個部分沒什么代碼可講,屬于面向接口開發的典范,要講就該去講 Okhttp 框架的源碼了。
這個部分引出了 Retrofit 的開源擁有者-Square 公司的另一個優秀的開源項目 Okhttp,是不是也很想一探究竟?