Retrofit2已經面世好久了,有不少好的文章分析過,這篇文章我只想記錄本身閱讀Retrofit 2.3.0源碼後的分析過程,如何閱讀源碼以及分析我以爲是最重要的。java
以前作過Gank的客戶端,所以直接用Gank網站的請求了。git
構建retrofit對象:github
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://gank.io")
.build();
複製代碼
定義請求interface, 能夠把它內部的每個接口方法看作是一個封裝了請求url及參數的方法,其返回值是可執行請求的對象,而在 Retrofit 中默認是 Call 可執行對象,也就是說 call 調用某個方法,如 enqueue 就能夠異步執行請求。設計模式
public interface ApiService {
@GET("api/data/{type}/{size}/{page}")
Call<ResponseBody> getArticles(@Path("type") String type, @Path("size") int size,
@Path("page") int page);
}
複製代碼
生成代理對象:api
ApiService apiService = retrofit.create(ApiService.class);
複製代碼
獲取 call 這個可執行請求的對象,並enqueue異步執行請求:緩存
Call<ResponseBody> call = apiService.getArticles("Android",10, 1);
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
try {
Log.d(TAG,response.body().string());
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
}
});
複製代碼
call.enqueue會執行真正的請求,而且內部會切換到主線程回調到 onResponse 方法內可拿到最終結果。bash
附上 官方Retrofit使用文檔數據結構
Retrofit本質上是對okhttp3的封裝,其最大的優點就是解耦作的很是好,接下來就從源碼角度分析下。異步
構建Retrofit用到了 Builder 模式,很適合自定義一些東西。先走到 baseUrl 方法內看下:ide
public Builder baseUrl(String baseUrl) {
checkNotNull(baseUrl, "baseUrl == null");
HttpUrl httpUrl = HttpUrl.parse(baseUrl);
if (httpUrl == null)
throw new IllegalArgumentException("Illegal URL: " + baseUrl);
}
return baseUrl(httpUrl);
}
複製代碼
將字符串類型的url解析成 HttpUrl對象,這個對象能夠理解爲拆分了url的協議、域名、端口、路徑等變量並保存在了一個對象中,須要哪一個部分就能夠隨時拿出,最後保存下 httpUrl 變量。
最簡單的就是設置一個地址,而後直接調用 build 方法:
public Retrofit build() {
if (baseUrl == null) {
throw new IllegalStateException("Base URL required.");
}
okhttp3.Call.Factory callFactory = this.callFactory;
if (callFactory == null) {
callFactory = new OkHttpClient();
}
Executor callbackExecutor = this.callbackExecutor;
if (callbackExecutor == null) {
callbackExecutor = platform.defaultCallbackExecutor();
}
// Make a defensive copy of the adapters and add the default Call adapter.
List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories);
adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));
// Make a defensive copy of the converters.
List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories);
return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
callbackExecutor, validateEagerly);
}
複製代碼
若是不在構建Retrofit時設置 client 那麼就會默認建立一個 OkHttpClient, 前面已經說過 Retrofit 是對 okhttp 的封裝而已,本質上仍是okhttp 進行請求,而如今只是分析 Retrofit, 所以不打算深刻OkHttpClient, 可是根據它的接口類型也能判斷是一個 生產okhttp中的Call對象的工廠, 而Call對象就是可執行任務的對象, Retrofit 中也有 Call 致使有點混亂。。須要好好辨別下。
若不設置 callbackExecutor 也會經過 platform.defaultCallbackExecutor() 建立一個默認的,經過看源碼發現Retrofit也就支持兩個平臺,一個是Java8,一個就是Android :
static class Android extends Platform {
@Override public Executor defaultCallbackExecutor() {
return new MainThreadExecutor();
}
@Override CallAdapter.Factory defaultCallAdapterFactory(@Nullable Executor
callbackExecutor) {
if (callbackExecutor == null) throw new AssertionError();
return new ExecutorCallAdapterFactory(callbackExecutor);
}
static class MainThreadExecutor implements Executor {
private final Handler handler = new Handler(Looper.getMainLooper());
@Override public void execute(Runnable r) {
handler.post(r);
}
}
}
複製代碼
它會返回一個 MainThreadExecutor 對象,而它的實現很是簡單,就是經過Handler將線程切換到主線程,記住這個 callbackExecutor , 它會在某個地方執行 execute 方法這個時候會切到主線程進行回調。
adapterFactories列表對象能夠把它理解爲 生產 adapter 的工廠,而 adapter 從名字上來看是適配器,它調用其中的 adapt 方法返回的就是可執行對象!在 Retrofit 裏默認的實現就是 Call 對象,而且因爲 Retrofit 的神奇解耦,它能夠自定義任何CallAdapter 和 Factory,最有名的就是RxJava,其返回的可執行對象變成了Observable而已 ,列表表示能夠添加多個 callAdapter工廠, 根據你寫的接口方法的返回值判斷選擇哪一個適配器。
默認狀況下調用 platform.defaultCallbackExecutor() 建立 ExecutorCallAdapterFactory ,這個類很關鍵,工廠顧名思義就是生產CallAdapter的,其中的 get 方法返回了一個 CallAdapter 對象,這個對象會在某個關鍵時刻調用 adapt 從而返回 Call 對象,這個調用流程以後再詳解。
converterFactories 轉換器工廠,其套路和 adapterFactories 很是相似,它的做用就是將請求後返回的數據轉換成你想要的數據結構,應用最廣的應該是 GsonConverterFactory , 能夠將結果用 Gson 轉換成自定義好的數據結構。其默認實現是 BuiltInConverters ,這個默認的工廠返回的 Converter 基本沒作什麼事情,基本只是把 okhttp 返回給 Retrofit 的 ResponseBody 數據結構返回出去。所以暫時不須要太過在乎。
最後以這些對象做爲參數傳給 Retrofit 構造一個 Retrofit 實例。總結下關鍵的幾個對象:將 url 解析成 HttpUrl 對象並保存供之後使用;在不自定義的狀況下默認建立 callFactory, 這個 OkHttpClient 的實例,最終會經過這個工廠生成 okhttp 中的 call 對象來執行真正的請求;callbackExecutor默認實現是 MainThreadExecutor 是用來最後切換到主線程用的;adapterFactories用來生產adapter,默認是 ExecutorCallAdapterFactory,最後會返回 Call 對象;converterFactories默認實現是 BuiltInConverters, 基本上就是將 okhttp 返回的 ResponseBody 返回出去。
動態代理這個能夠說是 Retrofit 最精華的地方了。再此以前須要理解反射和動態代理,推薦兩篇不錯的文章:反射、動態代理。
構建完 Retrofit 對象後,須要調用 retrofit.create(ApiService.class), 生成 ApiService 接口對應的動態代理對象。
public <T> T create(final Class<T> service) {
// ......
return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
new InvocationHandler() {
// 獲取目前的平臺,對於咱們來講就是Android平臺
private final Platform platform = Platform.get();
@Override public Object invoke(Object proxy, Method method, @Nullable Object[] args)
throws Throwable {
// ......
// 構建 接口方法 對象,此對象主要是解析註解並拼接成請求所需的參數。
// 它是最後用來給okhttp使用並真正發送請求。
ServiceMethod<Object, Object> serviceMethod =
(ServiceMethod<Object, Object>) loadServiceMethod(method);
// 本質上就是Retrofit對Okhttp的Call對象進一步的封裝
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
// 最後默認返回的也是一個Call可執行任務對象(固然RxJava返回的對象就不是默認的了),
// 其實相似因而OkHttpCall的代理類,只不過內部加了切換到主線程的操做。
return serviceMethod.callAdapter.adapt(okHttpCall);
}
});
}
複製代碼
部分沒啥用的代碼省略了,剩下的都是關鍵代碼,而且加了註釋。這裏再說下動態代理相關的,create 方法返回值是第一部分的使用步驟中的 apiService, 它是動態代理生成的對象。調用動態代理對象的方法後會調到 invoke 方法,返回值對應使用步驟中的 call 對象。
進入 loadServiceMethod 方法,能夠看到對 serviceMethod 會有一個緩存,一個方法只會解析一次,以後重複利用。而後經過 build 構建一個 ServiceMethod 對象。
public ServiceMethod build() {
// 跟進 createCallAdapter 能夠看到它返回的 CallAdapter 是根據方法的返回類型,
// 如本文使用步驟中返回類型是 Call,那麼就會返回以前已建立過的默認 CallAdapter
callAdapter = createCallAdapter();
// 返回結果類型,對於 Retrofit 默認返回結果類型是 ResponseBody,
// 所以在閱讀源碼時直接把 responseType 看作 ResponseBody 類型。
responseType = callAdapter.responseType();
// ......
// 跟進 createResponseConverter 方法能夠看到它是根據 responseType 返回對應的 Converter。
responseConverter = createResponseConverter();
// 遍歷解析方法上的註解如 @GET、@POST等(拆分註解中的字符串,將參數名記錄)
for (Annotation annotation : methodAnnotations) {
parseMethodAnnotation(annotation);
}
// ......
for (int p = 0; p < parameterCount; p++) {
Type parameterType = parameterTypes[p];
// ......
Annotation[] parameterAnnotations = parameterAnnotationsArray[p];
// ......
// 遍歷解析形參的註解,如@Path、@Query等.
parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);
}
// ......
return new ServiceMethod<>(this);
}
複製代碼
其實 ServiceMethod 不是理解 Retrofit 的關鍵,只需知道它封裝瞭解析註解邏輯和記錄參數。
直接看下 OkhttpCall 的enqueue方法
@Override public void enqueue(final Callback<T> callback) {
okhttp3.Call call;
synchronized (this) {
// ......
// 這裏的 call 就是 OkHttp 的 call
call = rawCall = createRawCall();
}
// ......
// okhttp 的 call 異步執行並回調,注意這裏依然在異步線程中。
call.enqueue(new okhttp3.Callback() {
@Override public void onResponse(okhttp3.Call call, okhttp3.Response rawRespon
throws IOException {
Response<T> response;
try {
response = parseResponse(rawResponse);
} catch (Throwable e) {
callFailure(e);
return;
}
callSuccess(response);
}
// ....
private void callSuccess(Response<T> response) {
try {
callback.onResponse(OkHttpCall.this, response);
} catch (Throwable t) {
t.printStackTrace();
}
}
});
}
複製代碼
最後一步經過 callAdapter.adapt(okHttpCall) 返回一個最終可執行對象,這裏咱們都是看默認的,所以 callAdapter 是 ExecutorCallAdapterFactrory 中 get 獲取的 callAdapter, 其 adapter 方法返回的是 ExecutorCallbackCall 也是實現了 Call 接口,以前說過此對象是 okHttpCall 的代理對象,所以傳入 okHttpCall 實例,而內部乾的最關鍵的一件事就是在異步執行請求完成後經過 callBackExecutor(以前早已準備好的Handler切換) 切換到主線程。
綠色線框表明咱們使用 Retrofit 須要作的幾步。
Retrofit 內部有大量的設計模式,設計的很是巧妙,多看源碼也能提升咱們的代碼設計能力。build 階段用了構造者模式,create 用了動態代理模式,CallAdapterFactory 和 ConverterFactory 用了適配器模式,等等。
爲了加深對Retrofit的理解以及體會它的好用程度,寫了一個自定義ConverterFactory。
public final class StringConverterFactory extends Converter.Factory {
private final static String TAG = "StringConverterFactory";
@Nullable
@Override
public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
if(type == String.class){
return StringResponseConverter.INSTANCE;
}
return null;
}
@Nullable
@Override
public Converter<?, RequestBody> requestBodyConverter(Type type, Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
if(type == RequestBody.class){
return StringRequestBodyConverter.INSTANCE;
}
return null;
}
final static class StringResponseConverter implements Converter<ResponseBody, String> {
final static StringResponseConverter INSTANCE = new StringResponseConverter();
@Override
public String convert(ResponseBody value) throws IOException {
Log.d(TAG, value.toString());
return value.string();
}
}
final static class StringRequestBodyConverter implements Converter<RequestBody, RequestBody> {
final static StringRequestBodyConverter INSTANCE = new StringRequestBodyConverter();
@Override
public RequestBody convert(RequestBody value) throws IOException {
Log.d(TAG, "no change, hahaha...");
return value;
}
}
}
複製代碼
支持String的泛型參數,其實內部也就是把 ResponseBody 提早轉成 String 並打印而已。在使用的時候須要在構建 Retrofit 時添加:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://gank.io")
.addConverterFactory(new StringConverterFactory())
.build();
複製代碼
最後對 Retrofit 作一個總結 。Retrofit 是對 okhttp3 的封裝,其最大的特性就是解耦,你能夠自定義不少你想要的東西,最知名的就是 RxJava 的配合使用。
首先調用 build 方法建立生產 okhttp3 的 call 對象的callFactory、建立用來切換線程的 callBackExecutor、建立生產 CallAdapter 的 CallAdapterFactory、建立生產 Converter 的 ConverterFactory。
接着調用 retrofit.create 建立動態代理對象,調用接口方法會觸發 invoke 方法,invoke 內建立 ServiceMethod並作了註解解析、建立OkhttpCall(對okhttp3的call進一步封裝),最後經過 CallAdapter.adapt 方法返回可執行對象默認是 ExecutorCallBackCall。
最後調用 call.enqueue 後 okhttp3將請求 ServiceMethod 解析好的url和參數,最終返回結果會被 Converter.convert 解析成你想要的數據模型(默認是ResponseBody),最後經過 callBackExecutor.execute 切換到主線程將數據回調給開發者。
ps:作完 Retrofit 源碼分析後,還想看下 RxJava 的源碼,爲了更深刻理解 RxJava 和 Retrofit 共同使用的原理。最近想到一句話感受蠻有意思的:當你不知道作什麼的時候,去看看源碼或官方文檔吧。