Retrofit解析
一、整體思路
從使用方法出發,首先是怎麼使用,其次是我們使用的功能在內部是如何實現的,實現方案上有什麼技巧,有什麼正規化。全文基本上是對 Retrofit 原始碼的一個分析與導讀,非常建議大家下載 Retrofit 原始碼之後,跟著本文,過一遍原始碼。
二、基本用例
2.1 建立 Retrofit 物件
Retrofit retrofit = new Retrofit.Builder() .baseUrl("https://api.github.com/") .addConverterFactory(GsonConverterFactory.create()) .build();
builder 模式,外觀模式(門面模式),這就不多說了,可以看看 stay 的 Retrofit分析-經典設計模式案例 這篇文章。
2.2 定義 API 並獲取 API 例項
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
呀,當然可以,但是可靠地自動生成豈不更方便?
2.3 呼叫 API 方法
獲取到 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 normal invocation. if (method.getDeclaringClass() == Object.class) { return method.invoke(this, args); } if (platform.isDefaultMethod(method)) { return platform.invokeDefaultMethod(method, service, proxy, 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);
在繼續分析這三行程式碼之前,我們先看看 Stay 在 Retrofit分析-漂亮的解耦套路 這篇文章中分享的流程圖,完整的流程概覽建議仔細看看這篇文章:

retrofit_stay.png
這三行程式碼基本就是對應於流程圖中軸上部了, ServiceMethod
, build OkHttpCall
, CallAdapter adapt
。
2.4 ServiceMethod
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 請求時設定引數;
它們的使用稍後再分析,這裡先看看它們的建立(程式碼比較分散,就不貼太多程式碼了,大多是結論):
2.4.1 callFactory
this.callFactory = builder.retrofit.callFactory()
,所以 callFactory
實際上由 Retrofit
類提供,而我們在構造 Retrofit
物件時,可以指定 callFactory
,如果不指定,將預設設定為一個 okhttp3.OkHttpClient
。
2.4.2 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
物件時進行新增。
2.4.3, responseConverter
private Converter<ResponseBody, T> createResponseConverter() { Annotation[] annotations = method.getAnnotations(); try { return retrofit.responseBodyConverter(responseType, annotations); } catch (RuntimeException e) { // Wide exception range because factories are user code. throw methodError(e, "Unable to create converter for %s", responseType); } }
同樣, responseConverter
還是由 Retrofit
類提供,而在其內部,邏輯和建立 callAdapter
基本一致,通過遍歷 Converter.Factory
列表,看看有沒有工廠能夠提供需要的 responseBodyConverter。工廠列表同樣可以在構造 Retrofit
物件時進行新增。
2.4.4 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
物件時進行新增。
2.4.5 工廠讓各個模組得以高度解耦
上面提到了三種工廠: okhttp3.Call.Factory
, CallAdapter.Factory
和 Converter.Factory
,分別負責提供不同的模組,至於怎麼提供、提供何種模組,統統交給工廠,Retrofit 完全不摻和,它只負責提供用於決策的資訊,例如引數/返回值型別、註解等。
這不正是我們苦苦追求的 高內聚低耦合 效果嗎?解耦的第一步就是 面向介面程式設計 ,模組之間、類之間通過介面進行依賴, 建立怎樣的例項,則交給工廠負責 ,工廠同樣也是介面,新增(Retrofit doc 中使用 install 安裝一詞,非常貼切)怎樣的工廠,則在最初構造 Retrofit
物件時決定,各個模組之間完全解耦, 每個模組只專注於自己的職責 ,全都是套路,值得反覆玩味、學習與模仿。
除了上面重點分析的這四個成員, ServiceMethod
中還包含了 API 方法的 url 解析等邏輯,包含了眾多關於泛型和反射相關的程式碼,有類似需求的時候,也非常值得學習模仿。
2.5 OkHttpCall
終於把 ServiceMethod
看了個大概,接下來我們看看 OkHttpCall
。
OkHttpCall
實現了 retrofit2.Call
,我們通常會使用它的 execute()
和 enqueue(Callback<T> callback)
介面。前者用於同步執行 HTTP 請求,後者用於非同步執行。
2.5.1,先看 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()); } 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; } Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException { ResponseBody rawBody = rawResponse.body(); // Remove the body's source (the only stateful object) so we can pass the response along. rawResponse = rawResponse.newBuilder() .body(new NoContentResponseBody(rawBody.contentType(), rawBody.contentLength())) .build(); int code = rawResponse.code(); if (code < 200 || code >= 300) { // ...返回錯誤 } if (code == 204 || code == 205) { return Response.success(null, rawResponse); } ExceptionCatchingRequestBody catchingBody = new ExceptionCatchingRequestBody(rawBody); try { T body = serviceMethod.toResponse(catchingBody); return Response.success(body, rawResponse); } catch (RuntimeException e) { // ...異常處理 } }
主要包括三步:
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
也派上了用場。
好了,之前準備好的東西都派上了用場,還好沒有白費 :)
2.5.2 再看 enqueue(Callback<T> callback)
這裡的非同步交給了 okhttp3.Call#enqueue(Callback responseCallback)
來實現,並在它的 callback 中呼叫 parseResponse
解析響應資料,並轉發給傳入的 callback。
2.6 CallAdapter
終於到了最後一步了, CallAdapter<T>#adapt(Call<R> call)
函式負責把 retrofit2.Call<R>
轉為 T
。這裡 T
當然可以就是 retrofit2.Call<R>
,這時我們直接返回引數就可以了,實際上這正是 DefaultCallAdapterFactory
建立的 CallAdapter
的行為。至於其他型別的工廠返回的 CallAdapter
的行為,這裡暫且不表,後面再單獨分析。
至此,一次對 API 方法的呼叫是如何構造併發起網路請求、以及解析返回資料,這整個過程大致是分析完畢了。對整個流程的概覽非常重要,結合 stay 畫的流程圖,應該能夠比較輕鬆地看清整個流程了。
雖然我們還沒分析完,不過也相當於到了萬里長征的遵義,終於可以舒一口氣了 :)
三、retrofit-adapters 模組
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 CallOnSubscribe<>(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>> subscriber) { // Since Call is a one-shot type, clone it for each new subscriber. 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); }
程式碼很簡短,只幹了三件事:
okhttp3.Call RequestArbiter
簡言之,大部分情況下 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 already 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 相關的邏輯非常簡單,看看 Operator 併發原語: producers(二),SingleDelayedProducer 就能懂了,這裡就不在贅述。實際幹活的邏輯就是執行 call.execute()
,並把返回值傳送給下游。
而 OperatorMapResponseToBodyOrError#call
也相當簡短:
@Override public Subscriber<? super Response<T>> call(final Subscriber<? super 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
時的呼叫棧:

retrofit_rxjava_call_stack.png
執行路徑就是:
Observable.subscribe CallOnSubscribe#call RequestArbiter#request OperatorMapResponseToBodyOrError$1#onNext
四、retrofit-converters 模組
retrofit 模組內建了 BuiltInConverters
,只能處理 ResponseBody
, RequestBody
和 String
型別的轉化(實際上不需要轉)。而 retrofit-converters 中的子模組則提供了 JSON,XML,ProtoBuf 等型別資料的轉換功能,而且還有多種轉換方式可以選擇。這裡我主要關注 GsonConverterFactory
。
程式碼非常簡單:
@Override public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) { TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type)); return new GsonResponseBodyConverter<>(gson, adapter); } final class GsonResponseBodyConverter<T> implements Converter<ResponseBody, 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 IOException { JsonReader jsonReader = gson.newJsonReader(value.charStream()); try { return adapter.read(jsonReader); } finally { value.close(); } } }
根據目標型別,利用 Gson#getAdapter
獲取相應的 adapter,轉換時利用 Gson 的 API 即可。