從使用方法出發,首先是怎麼使用,其次是咱們使用的功能在內部是如何實現的, 實現方案上有什麼技巧,有什麼範式。全文基本上是對 Retrofit 源碼的一個分析與 導讀,很是建議你們下載 Retrofit 源碼以後,跟着本文,過一遍源碼。java
上圖知識彙總的PDF相關內容後續GitHub更新,想衝擊金三銀四的小夥伴能夠找找看看,歡迎star
(順手留下GitHub連接,須要獲取相關面試等內容的能夠本身去找)
https://github.com/xiangjiana/Android-MS
(VX:mm14525201314)git
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 實例的呢?github
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 實例以後,調用方法和普通的代碼沒有任何區別:json
Call<List<Repo>> call = github.listRepos("square"); List<Repo> repos = call.execute().body();
這兩行代碼就發出了 HTTP 請求,並把返回的數據轉化爲了 List<Repo>
,太方 便了!api
如今咱們來看看調用 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 請求時設置參數;
它們的使用稍後再分析,這裏先看看它們的建立(代碼比較分散,就不貼太多代碼 了,大可能是結論):
callFactory
this.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 便可。
上圖知識彙總的PDF相關內容後續GitHub更新,想衝擊金三銀四的小夥伴能夠找找看看,歡迎star
(順手留下GitHub連接,須要獲取相關面試等內容的能夠本身去找)
https://github.com/xiangjiana/Android-MS
(VX:mm14525201314)