從使用方法出發,首先是怎么使用,其次是我們使用的功能在內部是如何實現的, 實現方案上有什么技巧,有什么范式。全文基本上是對 Retrofit 源碼的一個分析與 導讀,非常建議大家下載 Retrofit 源碼之后,跟著本文,過一遍源碼
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.addConverterFactory(GsonConverterFactory.create())
.build(); public interface GitHubService {
@GET("users/{user}/repos")
Call<List<Repo>> listRepos(@Path("user") String user);
}
GitHubService github = retrofit.create(GitHubService.class);先看定義,非常簡潔,也沒有什么特別之處,除了兩個注解:@GET和 @Path 。它們的用處稍后再分析,我們接著看創建 API 實 例: retrofit.create(GitHubService.class) 。這樣就創建了 API 實例了, 就可以調用 API 的方法發起 HTTP 網絡請求了,太方便了。 但 create 方法是怎么創建 API 實例的呢?
public <T> T create(final Class<T> service) {
// 省略非關鍵代碼
return (T) Proxy.newProxyInstance(service.getClassLoader(),
new Class<?>[] { service },
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object ... args)
throws Throwable {
// 先省略實現
}
});
}創建 API 實例使用的是動態代理技術。
簡而言之,就是動態生成接口的實現類(當然生成實現類有緩存機制),并創建其 實例(稱之為代理),代理把對接口的調用轉發給 InvocationHandler 實例, 而在 InvocationHandler 的實現中,除了執行真正的邏輯(例如再次轉發給真 正的實現類對象),我們還可以進行一些有用的操作,例如統計執行時間、進行初 始化和清理、對接口調用進行檢查等。 為什么要用動態代理?因為對接口的所有方法的調用都會集中轉發到 InvocationHandler#invoke 函數中,我們可以集中進行處理,更方便了。你可 能會想,我也可以手寫這樣的代理類,把所有接口的調用都轉發到 InvocationHandler#invoke 呀,當然可以,但是可靠地自動生成豈不更方便?
獲取到 API 實例之后,調用方法和普通的代碼沒有任何區別:
Call<List<Repo>> call = github.listRepos("square");
List<Repo> repos = call.execute().body();這兩行代碼就發出了 HTTP 請求,并把返回的數據轉化為了 List<Repo>,太方 便了!
現在我們來看看調用 listRepos 是怎么發出 HTTP 請求的。上面 Retrofit#create 方法返回時省略的代碼如下:
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, Object.. . args)
throws Throwable {
// If the method is a method from Object then defer to n ormal invocation.
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
if (platform.isDefaultMethod(method)) {
return platform.invokeDefaultMethod(method, service, p roxy, args);
}
ServiceMethod serviceMethod = loadServiceMethod(method);
OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
}
});如果調用的是 Object 的方法,例如 equals, toString,那就直接調用。 如果是 default 方法(Java 8 引入),就調用 default 方法。這些我們都先不管,因 為我們在安卓平臺調用 listRepos ,肯定不是這兩種情況,那這次調用真正干活 的就是這三行代碼了(好好記住這三行代碼,因為接下來很長的篇幅都是在講它們 :) ):
ServiceMethod serviceMethod = loadServiceMethod(method);
OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);在繼續分析這三行代碼之前,先看一個流程圖
這三行代碼基本就是對應于流程圖中軸上部了, ServiceMethod , build OkHttpCall, CallAdapter adapt。
ServiceMethod<T> 類的作用正如其 JavaDoc所言:
Adapts an invocation of an interface method into an HTTP call. 把對接口方法 的調用轉為一次 HTTP 調用。
一個 ServiceMethod 對象對應于一個 API interface 的一個方 法, loadServiceMethod(method) 方法負責加載 ServiceMethod :
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;
}這里實現了緩存邏輯,同一個 API 的同一個方法,只會創建一次。這里由于我們每 次獲取 API 實例都是傳入的 class 對象,而 class 對象是進程內單例的,所 以獲取到它的同一個方法 Method 實例也是單例的,所以這里的緩存是有效的。
我們再看看 ServiceMethod 的構造函數:
ServiceMethod(Builder<T> builder) {
this.callFactory = builder.retrofit.callFactory();
this.callAdapter = builder.callAdapter;
this.baseUrl = builder.retrofit.baseUrl();
this.responseConverter = builder.responseConverter;
this.httpMethod = builder.httpMethod;
this.relativeUrl = builder.relativeUrl;
this.headers = builder.headers;
this.contentType = builder.contentType;
this.hasBody = builder.hasBody;
this.isFormEncoded = builder.isFormEncoded;
this.isMultipart = builder.isMultipart;
this.parameterHandlers = builder.parameterHandlers;
}成員很多,但這里我們重點關注四個成 員: callFactory, callAdapter , responseConverter 和 parameterHandlers 。
callFactory負責創建 HTTP 請求,HTTP 請求被抽象為了okhttp3.Call類,它表示一個已經準備好,可以隨時執行的 HTTP 請求;callAdapter把retrofit2.Call<T>轉為 T (注意和okhttp3.Call區分開來,retrofit2.Call<T>表示的是對一個 Retrofit 方法的調用),這個過程會發送一個 HTTP 請求,拿到服務器返回的數據(通 過okhttp3.Call實現),并把數據轉換為聲明的 T 類型對象(通過Converter<F, T>實現);responseConverter是Converter<ResponseBody, T>類型,負責把服 務器返回的數據(JSON、XML、二進制或者其他格式,由ResponseBody封裝)轉化為 T 類型的對象;parameterHandlers則負責解析 API 定義時每個方法的參數,并在構造 HTTP 請求時設置參數;
它們的使用稍后再分析,這里先看看它們的創建(代碼比較分散,就不貼太多代碼 了,大多是結論):
callFactorythis.callFactory = builder.retrofit.callFactory(),所以 callFactory 實際上由 Retrofit 類提供,而我們在構造 Retrofit 對象 時,可以指定 callFactory,如果不指定,將默認設置為一個 okhttp3.OkHttpClient。
callAdapter private CallAdapter<?> createCallAdapter() {
// 省略檢查性代碼
Annotation[] annotations = method.getAnnotations();
try {
return retrofit.callAdapter(returnType, annotations);
} catch (RuntimeException e) {
// Wide exception range because factories are user code.
throw methodError(e, "Unable to create call adapter for %s", returnType);
}
}可以看到, callAdapter 還是由 Retrofit 類提供。在 Retrofit 類內部, 將遍歷一個 CallAdapter.Factory 列表,讓工廠們提供,如果最終沒有工廠能 (根據 returnType 和 annotations )提供需要的 CallAdapter ,那將拋出 異常。而這個工廠列表我們可以在構造 Retrofit 對象時進行添加。
responseConverter private Converter<ResponseBody, T> createResponseConverter() {
Annotation[] annotations = method.getAnnotations();
try {
return retrofit.responseBodyConverter(responseType, annotati ons);
} catch (RuntimeException e) {
// Wide exception range because factories are user code.
throw methodError(e, "Unable to create converter for %s", re sponseType);
}
}同樣, responseConverter 還是由 Retrofit 類提供,而在其內部,邏輯和創 建 callAdapter 基本一致,通過遍歷 Converter.Factory列表,看看有沒有 工廠能夠提供需要的 responseBodyConverter。工廠列表同樣可以在構造 Retrofit 對象時進行添加。
parameterHandlers每個參數都會有一個 ParameterHandler ,由 ServiceMethod#parseParameter 方法負責創建,其主要內容就是解析每個參數 使用的注解類型(諸如 Path , Query , Field 等),對每種類型進行單獨的 處理。構造 HTTP 請求時,我們傳遞的參數都是字符串,那 Retrofit 是如何把我們 傳遞的各種參數都轉化為 String 的呢?還是由 Retrofit 類提供 converter!
Converter.Factory 除了提供上一小節提到的 responseBodyConverter,還提 供 requestBodyConverter 和 stringConverter,API 方法中除了 @Body 和 @Part 類型的參數,都利用 stringConverter 進行轉換,而 @Body 和 @Part 類型的參數則利用 requestBodyConverter 進行轉換。
這三種 converter 都是通過“詢問”工廠列表進行提供,而工廠列表我們可以在構造 Retrofit 對象時進行添加。
上面提到了三種工廠: okhttp3.Call.Factory , CallAdapter.Factory 和 Converter.Factory ,分別負責提供不同的模塊,至于怎么提供、提供何種模 塊,統統交給工廠,Retrofit 完全不摻和,它只負責提供用于決策的信息,例如參 數/返回值類型、注解等。
這不正是我們苦苦追求的高內聚低耦合效果嗎?解耦的第一步就是面向接口編程, 模塊之間、類之間通過接口進行依賴,創建怎樣的實例,則交給工廠負責,工廠同 樣也是接口,添加(Retrofit doc 中使用 install 安裝一詞,非常貼切)怎樣的工 廠,則在最初構造 Retrofit 對象時決定,各個模塊之間完全解耦,每個模塊只 專注于自己的職責,全都是套路,值得反復玩味、學習與模仿。
除了上面重點分析的這四個成員, ServiceMethod 中還包含了 API 方法的 url 解 析等邏輯,包含了眾多關于泛型和反射相關的代碼,有類似需求的時候,也非常值 得學習模仿
終于把 ServiceMethod 看了個大概,接下來我們看看 OkHttpCall 。 OkHttpCall 實現了 retrofit2.Call ,我們通常會使用它的 execute() 和 enqueue(Callback<T> callback) 接口。前者用于同步執行 HTTP 請求,后者 用于異步執行。
execute() @Override
public Response<T> execute() throws IOException {
okhttp3.Call call;
synchronized (this) {
// 省略部分檢查代碼
call = rawCall;
if (call == null) {
try {
call = rawCall = createRawCall();
} catch (IOException | RuntimeException e) {
creationFailure = e;
throw e;
}
}
}
return parseResponse(call.execute());
......
}主要包括三步:
- 創建
okhttp3.Call,包括構造參數;- 執行網絡請求;
- 解析網絡請求返回的數據;
createRawCall() 函數中,我們調用了 serviceMethod.toRequest(args) 來創建 okhttp3.Request ,而在后者中,我們之前準備好的 parameterHandlers 就派上了用場。
然后我們再調用 serviceMethod.callFactory.newCall(request) 來創建 okhttp3.Call ,這里之前準備好的 callFactory 同樣也派上了用場,由于工 廠在構造 Retrofit 對象時可以指定,所以我們也可以指定其他的工廠(例如使 用過時的 HttpURLConnection 的工廠),來使用其它的底層 HttpClient 實現。
我們調用 okhttp3.Call#execute() 來執行網絡請求,這個方法是阻塞的,執行 完畢之后將返回收到的響應數據。收到響應數據之后,我們進行了狀態碼的檢查, 通過檢查之后我們調用了 serviceMethod.toResponse(catchingBody) 來把響 應數據轉化為了我們需要的數據類型對象。在 toResponse 函數中,我們之前準 備好的 responseConverter 也派上了用場。
好了,之前準備好的東西都派上了用場,還好沒有白費 :)
enqueue(Callback<T> callback)這里的異步交給了 okhttp3.Call#enqueue(Callback responseCallback) 來 實現,并在它的 callback 中調用 parseResponse 解析響應數據,并轉發給傳入 的 callback。
CallAdapter終于到了最后一步了, CallAdapter<T>#adapt(Call<R> call) 函數負責把 retrofit2.Call<R> 轉為 T 。這里 T 當然可以就是 retrofit2.Call<R> ,這時我們直接返回參數就可以了,實際上這正是 DefaultCallAdapterFactory創建的 CallAdapter 的行為。至于其他類型的 工廠返回的 CallAdapter 的行為,這里暫且不表,后面再單獨分析。
至此,一次對 API 方法的調用是如何構造并發起網絡請求、以及解析返回數據,這 整個過程大致是分析完畢了。對整個流程的概覽非常重要,結合 stay 畫的流程圖, 應該能夠比較輕松地看清整個流程了。
雖然我們還沒分析完,不過也相當于到了萬里長征的遵義,終于可以舒一口氣了 :)
retrofit 模塊內置了 DefaultCallAdapterFactory 和 ExecutorCallAdapterFactory ,它們都適用于 API 方法得到的類型為 retrofit2.Call 的情形,前者生產的 adapter 啥也不做,直接把參數返回,后 者生產的 adapter 則會在異步調用時在指定的 Executor 上執行回調。
retrofit-adapters 的各個子模塊則實現了更多的工 廠: GuavaCallAdapterFactory , Java8CallAdapterFactory 和 RxJavaCallAdapterFactory 。這里我主要分析 RxJavaCallAdapterFactory ,下面的內容就需要一些 RxJava 的知識了,不過 我想使用 Retrofit 的你,肯定也在使用RxJava :)
RxJavaCallAdapterFactory#get 方法中對返回值的類型進行了檢查,只支持 rx.Single , rx.Completable 和 rx.Observable ,這里我主要關注對 rx.Observable 的支持。
RxJavaCallAdapterFactory#getCallAdapter 方法中對返回值的泛型類型進行 了進一步檢查,例如我們聲明的返回值類型為 Observable<List<Repo>>,泛型 類型就是 List<Repo> ,這里對 retrofit2.Response 和retrofit2.adapter.rxjava.Result 進行了特殊處理,有單獨的 adapter 負責 進行轉換,其他所有類型都由 SimpleCallAdapter負責轉換。
那我們就來看看 SimpleCallAdapter#adapt :
@Override
public <R> Observable<R> adapt(Call<R> call) {
Observable<R> observable = Observable.create(new CallOnSubscri be<>(call))
.lift(OperatorMapResponseToBodyOrError.<R>instance()); if (scheduler != null) {
return observable.subscribeOn(scheduler);
}
return observable;
}這里創建了一個 Observable ,它的邏輯由 CallOnSubscribe 類實現,同時使 用了一個 OperatorMapResponseToBodyOrError 操作符,用來把 retrofit2.Response 轉為我們聲明的類型,或者錯誤異常類型。
我們接著看 CallOnSubscribe#call :
@Override
public void call(final Subscriber<? super Response<T>> subscribe r) {
// Since Call is a one-shot type, clone it for each new subscr iber.
Call<T> call = originalCall.clone();
// Wrap the call in a helper which handles both unsubscription and backpressure.
RequestArbiter<T> requestArbiter = new RequestArbiter<>(call, subscriber);
subscriber.add(requestArbiter);
subscriber.setProducer(requestArbiter);
}代碼很簡短,只干了三件事:
- clone 了原來的 call,因為
okhttp3.Call是只能用一次的,所以每次都是 新 clone 一個進行網絡請求;- 創建了一個叫做
RequestArbiter的producer,別被它的名字嚇懵了,它就 是個producer;- 把這個
producer設置給subscriber;
簡言之,大部分情況下 Subscriber 都是被動接受 Observable push 過來的數據, 但要是 Observable 發得太快,Subscriber 處理不過來,那就有問題了,所以就有 了一種 Subscriber 主動 pull 的機制,而這種機制就是通過 Producer 實現的。給 Subscriber 設置 Producer 之后(通過 Subscriber#setProducer 方法), Subscriber 就會通過 Producer 向上游根據自己的能力請求數據(通過 Producer#request 方法),而 Producer 收到請求之后(通常都是 Observable 管理 Producer,所以“相當于”就是 Observable 收到了請求),再根據請求的量給 Subscriber 發數據。
那我們就看看 RequestArbiter#request:
@Override
public void request(long n) {
if (n < 0) throw new IllegalArgumentException("n < 0: " + n);
if (n == 0) return; // Nothing to do when requesting 0.
if (!compareAndSet(false, true)) return; // Request was alread y triggered.
try {
Response<T> response = call.execute();
if (!subscriber.isUnsubscribed()) {
subscriber.onNext(response);
}
} catch (Throwable t) {
Exceptions.throwIfFatal(t);
if (!subscriber.isUnsubscribed()) {
subscriber.onError(t);
}
return;
}
if (!subscriber.isUnsubscribed()) {
subscriber.onCompleted();
}
}producer 相關的邏輯非常簡單,這里就不在贅述。實際干活的邏輯就是執行 call.execute() ,并把返回值發送給下游。
而 OperatorMapResponseToBodyOrError#call 也相當簡短:
@Override
public Subscriber<? super Response<T>> call(final Subscriber<? s uper T> child) {
return new Subscriber<Response<T>>(child) {
@Override
public void onNext(Response<T> response) {
if (response.isSuccessful()) {
child.onNext(response.body());
} else {
child.onError(new HttpException(response));
}
}
@Override
public void onCompleted() {
child.onCompleted();
}
@Override
public void onError(Throwable e) {
child.onError(e);
}
};
}關鍵就是調用了 response.body() 并發送給下游。這里, body() 返回的就是 我們聲明的泛型類型了,至于 Retrofit 是怎么把服務器返回的數據轉為我們聲明的 類型的,這就是 responseConverter 的事了,還記得嗎?
最后看一張返回 Observable 時的調用棧:
執行路徑就是:
Observable.subscribe,觸發 API 調用的執行;CallOnSubscribe#call,clone call,創建并設置producer;RequestArbiter#request,subscriber被設置了producer之后最終調用 request,在 request 中發起請求,把結果發給下游;OperatorMapResponseToBodyOrError$1#onNext,把response的 body 發 給下游;- 最終就到了我們
subscribe時傳入的回調里面了;
retrofit 模塊內置了 BuiltInConverters ,只能處理 ResponseBody , RequestBody 和 String 類型的轉化(實際上不需要轉)。而 retrofit- converters 中的子模塊則提供了 JSON,XML,ProtoBuf 等類型數據的轉換功能, 而且還有多種轉換方式可以選擇。這里我主要關注 GsonConverterFactory 。
代碼非常簡單:
@Override
public Converter<ResponseBody, ?> responseBodyConverter(Type typ e, Annotation[] annotations, Retrofit retrofit) {
TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
return new GsonResponseBodyConverter<>(gson, adapter);
}
final class GsonResponseBodyConverter<T> implements Converter<Re sponseBody, T> {
private final Gson gson;
private final TypeAdapter<T> adapter;
GsonResponseBodyConverter(Gson gson, TypeAdapter<T> adapter) {
this.gson = gson;
this.adapter = adapter;
}
@Override public T convert(ResponseBody value) throws IOExcept ion {
JsonReader jsonReader = gson.newJsonReader(value.charStream( ));
try {
return adapter.read(jsonReader); } finally {
value.close();
}
}
}根據目標類型,利用 Gson#getAdapter 獲取相應的 adapter,轉換時利用 Gson 的 API 即可。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。