Retrofit 原始碼剖析-深入
背景
前一章節,先系統的講解了關於Retrofit
實現當中的主要技術動態代理,本篇詳細結合動態代理在Retrofit
中的應用,擴充套件到結合RxJava
來使用
思路
要深入研究一款開源專案最好的入口就是它所暴露出來的外部使用介面,按照這個思路,所以需要大體先了解Retrofit
的基本使用,這裡就不闡述這些基礎的知識,可以檢視以前的部落格
專案分析
想要弄清楚 Retrofit 的細節,先來看一下 Retrofit 原始碼的組成:
-
一個 retrofit2.http 包,裡面全部是定義 HTTP 請求的註解,比如 GET、POST、PUT、DELETE、Headers、Path、Query 等等
-
餘下的 retrofit 2.0 包中十幾個類和介面就是全部 retrofit 的程式碼了,程式碼真的很少,很簡單,因為retrofit把網路請求這部分功能全部交給了 okHttp 了
初始物件
先上一段使用Retrofit開始前的初始程式碼
//手動建立一個OkHttpClient並設定超時時間快取等設定
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.connectTimeout(basePar.getConnectionTime(), TimeUnit.SECONDS);
/*建立retrofit物件*/
Retrofit retrofit = new Retrofit.Builder()
.client(builder.build())
.addConverterFactory(ScalarsConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.baseUrl(basePar.getBaseUrl())
.build();
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
這裡的入口Builder()
方法進入原始碼
public static final class Builder {
private Platform platform;
private okhttp3.Call.Factory callFactory;
private HttpUrl baseUrl;
private List<Converter.Factory> converterFactories = new ArrayList<>();
private List<CallAdapter.Factory> adapterFactories = new ArrayList<>();
private Executor callbackExecutor;
private boolean validateEagerly;
Builder(Platform platform) {
this.platform = platform;
// Add the built-in converter factory first. This prevents overriding its behavior but also
// ensures correct behavior when using converters that consume all types.
converterFactories.add(new BuiltInConverters());
}
public Builder() {
this(Platform.get());
}
xxxx
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
Builder類中主要是記錄請求過程中的一些配置資訊,比如基礎url
,當然重要的是addConverterFactory
方法記錄Converter
資料轉換器;addCallAdapterFactory
方法記錄CallAdapter-HTTP
請求返回資料的型別.
Converter
資料轉換器
Converter
資料轉換器預設存放在retrofit-converters
中
Converter
採用介面抽象的方式,靈活了轉換器的種類,常用的如上圖中的Gson
和String
,同樣這裡可以可以自由擴充套件,實現Converter.Factory
介面
CallAdapter
請求返回資料的型別
工程位置如圖
這裡同Converter
資料轉換器思想一樣同樣是採用抽象介面的方式,繼承CallAdapter.Factory
類實現擴充套件,正如上圖中箭頭指示的顯示擴充套件的RxJava2
的擴充套件
資訊的轉換
在Builder
記錄完資料後通過build()
方法,將收集的資訊傳遞給最後的Retrofit
物件中,並且初始了okhttp3.Call.Factory
請求處理類,從中可以看出Retrofit2.0
底層的http
請求預設就是通過okhttp3
處理的。
/**
* Create the {@link Retrofit} instance using the configured values.
* <p>
* Note: If neither {@link #client} nor {@link #callFactory} is called a default {@link
* OkHttpClient} will be created and used.
*/
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);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
動態代理
Retrofit
初始完成以後,需要呼叫create
方法傳入一個Java介面抽象類物件作為引數。其實這一步真是Retrofit
動態代理生成的地方
原始碼:
@SuppressWarnings("unchecked") // Single-interface proxy creation guarded by parameter safety.
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, 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);
}
});
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
使用程式碼:
HttpTestService httpService = retrofit.create(HttpTestService.class);
httpService.getAllVedioBy(isAll());
- 1
- 2
- 1
- 2
上面HttpTestService
物件其實是一個動態代理物件,並不是一個真正的 HttpTestService
介面的implements
物件,當 httpService
物件呼叫 getAllVedioBy
方法時,執行的是下面動態代理方法。
ServiceMethod serviceMethod = loadServiceMethod(method);
OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
- 1
- 2
- 3
- 1
- 2
- 3
其中主要ServiceMethod
是主要的處理物件
ServiceMethod
資訊類
當呼叫loadServiceMethod
方法,將抽象HttpTestService中通過註解定義的資訊收集起來存放於ServiceMethod
中,其中主要包含了一下資料:
-
OkHttpClient:傳送網路請求的工具
-
RequestFactory: 類似於 Volley 中的 Request,包含了HTTP請求的Url、Header資訊,MediaType、Method以及RequestAction陣列
-
CallAdapter:HTTP請求返回資料的型別
-
Converter:資料轉換器
最後Map<Method, ServiceMethod> serviceMethodCache
儲存全部的 ServiceMethod
資訊類
ServiceMethod<?, ?> loadServiceMethod(Method method) {
ServiceMethod<?, ?> result = serviceMethodCache.get(method);
if (result != null) return result;
synchronized (serviceMethodCache) {
result = serviceMethodCache.get(method);
if (result == null) {
result = new ServiceMethod.Builder<>(this, method).build();
serviceMethodCache.put(method, result);
}
}
return result;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
debug
可檢視當前ServiceMethod
物件收集的資訊
收集完資訊以後執行了
OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
- 1
- 1
這一步對okhttp熟悉的同學肯定知道了,就是通過okhttp請求資料,其中serviceMethod是請求的地址資訊,args是當前介面需要的引數資訊。
執行完http請求以後,執行了
serviceMethod.callAdapter.adapt(okHttpCall);
- 1
- 1
這一步是將okhttp獲取到的資料傳遞給一開始Retrofit
通過builder
指定的RxJavaCallAdapterFactory
類,那它是如何一步步處理的呢?
其實在一開始的獲取serviceMethod
方法loadServiceMethod(Method
method)
中,已經將Retrofit
物件傳遞給了serviceMethod
物件
核心程式碼:
//這裡的this即是`Retrofit`物件
result = new ServiceMethod.Builder(this, method).build();
- 1
- 2
- 1
- 2
通過.Builder(this, method).build()
方法將Retrofit
一開始的builder
資訊全部傳遞給了現在的serviceMethod
物件,所以當執行
serviceMethod.callAdapter.adapt(okHttpCall);
- 1
- 1
serviceMethod.callAdapter
便制動轉換成了RxJavaCallAdapterFactory
,呼叫CallAdapter
抽象介面adapt
進入到RxJavaCallAdapterFactory
類中
原始碼:
@Override public <R> Observable<Response<R>> adapt(Call<R> call) {
Observable<Response<R>> observable = Observable.create(new CallOnSubscribe<>(call));
if (scheduler != null) {
return observable.subscribeOn(scheduler);
}
return observable;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
最後生成RxJava
的Observable
物件,後面通過得到的Observable
物件就是對資料的處理了。
到此基於Retrofit
結合RxJava
原始碼的解讀就完成了,回頭看整個過程其實還是很簡單,作者條理清晰理解起來也很簡單,一口氣終於把Retrofit
大體擼了一遍。
總結
Retrofit
非常巧妙的用註解來描述一個 HTTP
請求,將一個 HTTP
請求抽象成一個 Java
介面,然後用了 Java
動態代理的方式,動態的將這個介面的註解“翻譯”成一個 HTTP
請求,最後再執行這個 HTTP
請求
Retrofit
的功能非常多的依賴 Java
反射,程式碼中其實還有很多細節,比如異常的捕獲、丟擲和處理,大量的 Factory
設計模式。
Retrofit
中介面設計的恰到好處,在你建立 Retrofit
物件時,讓你有更多更靈活的方式去處理你的需求,比如使用不同的 Converter
、使用不同的 CallAdapter
,這也就提供了你使用 RxJava
來呼叫 Retrofit
的可能。
專欄
原始碼
建議
相關文章
- 深入剖析RocketMQ原始碼-NameServerMQ原始碼Server
- 深入剖析(JDK)ArrayQueue原始碼JDK原始碼
- 深入剖析LinkedList原始碼原始碼
- 深入剖析Vue原始碼 - 元件基礎Vue原始碼元件
- 深入剖析Vue原始碼 - 元件進階Vue原始碼元件
- 深入剖析Vue原始碼 - 完整渲染過程Vue原始碼
- retrofit 原始碼分析原始碼
- Retrofit原始碼分析原始碼
- Retrofit 原始碼解析原始碼
- Retrofit原始碼解析原始碼
- Android主流三方庫原始碼分析(二、深入理解Retrofit原始碼)Android原始碼
- 深入剖析Vue原始碼 - 選項合併(上)Vue原始碼
- 深入剖析Vue原始碼 - 選項合併(下)Vue原始碼
- 深入剖析 RocketMQ 原始碼 - 訊息儲存模組MQ原始碼
- 深入剖析 RocketMQ 原始碼 - 負載均衡機制MQ原始碼負載
- 深入剖析Retrofit系列(一)來自官方的Retrofit開發手冊(中英互譯)
- Retrofit原始碼分析三 原始碼分析原始碼
- Java集合原始碼剖析——ArrayList原始碼剖析Java原始碼
- 【Java集合原始碼剖析】ArrayList原始碼剖析Java原始碼
- 【Java集合原始碼剖析】Vector原始碼剖析Java原始碼
- 【Java集合原始碼剖析】HashMap原始碼剖析Java原始碼HashMap
- 【Java集合原始碼剖析】Hashtable原始碼剖析Java原始碼
- 【Java集合原始碼剖析】TreeMap原始碼剖析Java原始碼
- RxJava + Retrofit原始碼解析RxJava原始碼
- 【Java集合原始碼剖析】LinkedList原始碼剖析Java原始碼
- 【Java集合原始碼剖析】LinkedHashmap原始碼剖析Java原始碼HashMap
- Retrofit原始碼解讀(一)--Retrofit簡單流程原始碼
- Android Retrofit原始碼解析Android原始碼
- Retrofit2.0- 原始碼分析原始碼
- Retrofit2原始碼解析原始碼
- 深入剖析Vue原始碼 - 例項掛載,編譯流程Vue原始碼編譯
- 深入剖析Vue原始碼 - 揭祕Vue的事件機制Vue原始碼事件
- 【原始碼SOLO】Retrofit2原始碼解析(一)原始碼
- 【原始碼SOLO】Retrofit2原始碼解析(二)原始碼
- epoll–原始碼剖析原始碼
- HashMap原始碼剖析HashMap原始碼
- Alamofire 原始碼剖析原始碼
- Handler原始碼剖析原始碼