一、引言
Retrofit
和 OKHttp
同爲 square
出品的網絡請求相關庫,不同的是 Retrofit
本身不進行網絡請求,而是作爲一個協調者,協調其他組件共同處理網絡請求。用官網描述來說就是:Retrofit
是可插拔的,它允許不同的執行機制和類庫用於執行HTTP
請求、允許不同序列化的類庫進行java
實體類與HTTP
響應數據之間轉換。
Retrofit
的網絡請求部分默認基於OkHttp
,關於OkHttp
,鄙人寫過 OkHttp源碼分析
一文,感興趣的童鞋可以看看。
本文純屬基於個人理解,源碼解析不限於執行流程,因此受限於知識水平,有些地方可能依然沒有理解到位,還請發現問題的童鞋理性指出。
溫馨提示:本文源碼基於
Retrofit-2.4.0
二、流程分析
1. 簡單使用
這裏以請求 玩Android 首頁數據爲例,演示使用Retrofit
進行網絡請求的最基本方式。
首先如下初始化 Retrofit
:
public void initializeRetrofit() {
retrofit = new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.baseUrl("http://wanandroid.com")
.build();
}
然後如下建立請求接口:
public interface Service{
@GET("article/list/{page}/json")
public Call<ResponseEntry<ResponseData>> getHomeList(@Path("page")int page);
}
接着如下調用請求、處理響應數據:
public void getHomeList(int page, ResCallback<ResponseEntry<ResponseData>> callback){
if (service == null)
service = retrofit.create(Service.class);
service.getHomeList(page).enqueue(new Callback<ResponseEntry<ResponseData>>() {
@Override
public void onResponse(Call<ResponseEntry<ResponseData>> call, Response<ResponseEntry<ResponseData>> response) {
System.out.println(response.message());
System.out.println(response.code());
System.out.println(response.headers());
if (response.isSuccessful()){
ResponseEntry<ResponseData> body = response.body();
if (body == null) {
callback.onFailed(new Exception("body is null !!"));
return;
}
callback.onSuccess(body);
}
}
@Override
public void onFailure(Call<ResponseEntry<ResponseData>> call, Throwable t) {
}
});
}
上面可以注意到的一點是,不同於直接使用OkHttp
,這裏response.body()
可以直接拿到我們需要的解析好的Java
實體類了,而不需要再做Json
數據解析工作, 它的使用過程如下:
而一般來說,我們使用OkHttp
進行網絡請求的使用過程如下:
顯然 Retrofit
的目的就是把網絡請求、響應數據解析等相互分離的操作都整合到一起,達到 All in one
的效果,而實際請求和解析都是以可插拔的插件形式存在,靈活度非常高。
2. 創建服務到建立Call
過程分析
關於 Retrofit
的構建 ,我們注意一下必填參數以及默認參數即可,根據如下Retrofit.Build#build
源碼可知:
-
baseUrl
必填 - 默認
callFactory
爲OkHttpClient
;默認callbackExecutor
(回調執行器)在Android
中是主線程的Handler
;默認會先添加Retrofit
內部的轉換器,然後是其他,比如我們自定義的轉換器,這是爲了避免內部轉換器的行爲被複寫掉,以及確保使用消耗(consume
)所有類型的轉換器時能有正確的行爲。
public Retrofit build() {
// baseUrl必填
if (baseUrl == null) {
throw new IllegalStateException("Base URL required.");
}
// 默認Call工廠爲 OkHttpClient
okhttp3.Call.Factory callFactory = this.callFactory;
if (callFactory == null) {
callFactory = new OkHttpClient();
}
// 默認回調執行器爲主線程Handler
Executor callbackExecutor = this.callbackExecutor;
if (callbackExecutor == null) {
callbackExecutor = platform.defaultCallbackExecutor();
}
List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor));
List<Converter.Factory> converterFactories = new ArrayList<>(
1 + this.converterFactories.size() + platform.defaultConverterFactoriesSize());
// 這裏會先添加Retrofit內部的轉換器再添加我們自定的轉換器
converterFactories.add(new BuiltInConverters());
converterFactories.addAll(this.converterFactories);
converterFactories.addAll(platform.defaultConverterFactories());
// ...
}
這裏關注一下Android
平臺的回調執行器,因爲回調執行在主線程的Handler
上,因此可以在回調中直接操作UI
控件。
static class Android extends Platform {
@Override public Executor defaultCallbackExecutor() {
return new MainThreadExecutor();
}
static class MainThreadExecutor implements Executor {
// UI線程
private final Handler handler = new Handler(Looper.getMainLooper());
@Override public void execute(Runnable r) {
handler.post(r);
}
}
// ...
}
接着來分析一下使用Retrofit#create
創建一個請求服務實例時發生了什麼,Retrofit#create
源碼如下,可知:
首先需要確定的是
service
本身是個接口,並且不繼承於其他接口。-
然後重點來了,
eagerlyValidateMethods
會通過反射獲取service
接口中所有的方法,接着嘗試從ServiceMethod
緩存池中查找對應於各個方法的ServiceMethod
,如果沒找到的話,則重新通過ServiceMethod.parseAnnotations
去解析各個方法的註解,解析完成後將返回的ServiceMethod
(這裏返回的ServiceMethod
其實是實現類HttpServiceMethod
,HttpServiceMethod
會負責根據解析的註解參數創建Call
,並在HttpServiceMethod#invoke
調用時執行網絡請求)存入緩存池中,方便後續複用,這裏緩存池的作用跟線程池的概念異曲同工,都是爲了減少因爲每次都解析(創建)而造成的不必要的性能損耗,所以乾脆花點內存存起來省事兒。eagerlyValidateMethods
執行過程如下: -
接着通過
Proxy.newProxyInstance
給服務接口創建一個代理實例,實際可轉成對應接口的類型,這裏主要關注一下InvocationHandler
, 每個Proxy
對象實例都會綁定一個InvocationHandler
對象,當執行Proxy#invok
方法時,最終對派發給InvocationHandler#invok
,也就是說,我們通過服務接口實例調用接口方法時,最終都會通過InvocationHandler#invok
去執行。invoke
方法執行鏈如下:
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();
private final Object[] emptyArgs = new Object[0];
@Override public Object invoke(Object proxy, Method method, @Nullable 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);
}
// 這裏實際最終執行的是 HttpServiceMethod#invoke(..)
return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
}
});
}
private void eagerlyValidateMethods(Class<?> service) {
Platform platform = Platform.get();
for (Method method : service.getDeclaredMethods()) {
// 在 Android 7.0 以前版本都是 false,Android 7.0 及以上則根據 `isDefaultMethod`的複寫值決定
if (!platform.isDefaultMethod(method)) {
loadServiceMethod(method);
}
}
}
ServiceMethod<?> loadServiceMethod(Method method) {
ServiceMethod<?> result = serviceMethodCache.get(method);
if (result != null) return result;
synchronized (serviceMethodCache) {
// 先從服務方法緩存中獲取
result = serviceMethodCache.get(method);
if (result == null) {
// 如果此前沒有解析,則重新解析註解
result = ServiceMethod.parseAnnotations(this, method);
// 然後將解析結果添加到緩存,以便後續複用
serviceMethodCache.put(method, result);
}
}
return result;
}
上面的 parseAnnotations
執行鏈如下:
我們順着這條鏈看看,首先是ServiceMethod#parseAnnotations
:
abstract class ServiceMethod<T> {
static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
// 1. 解析方法的註解參數,保存在 RequestFactory
RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);
// ...
// 2. 使用將上面解析的參數建立Call,用於網絡請求
return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
}
}
接着是RequestFactory#parseAnnotations
,源碼如下,主要做了三件事情,看註釋即可:
final class RequestFactory {
static RequestFactory parseAnnotations(Retrofit retrofit, Method method) {
return new Builder(retrofit, method).build();
}
static final class Builder{
RequestFactory build() {
// 1. 解析每個方法的註解
for (Annotation annotation : methodAnnotations) {
parseMethodAnnotation(annotation);
}
// ...
int parameterCount = parameterAnnotationsArray.length;
parameterHandlers = new ParameterHandler<?>[parameterCount];
// 2. 解析方法參數
for (int p = 0; p < parameterCount; p++) {
parameterHandlers[p] = parseParameter(p, parameterTypes[p], parameterAnnotationsArray[p]);
}
// ...
// 3. 創建 RequestFactory 保存參數
return new RequestFactory(this);
}
}
}
接着是 HttpServiceMethod#parseAnnotations
,源碼如下:
static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
Retrofit retrofit, Method method, RequestFactory requestFactory) {
// 1. 獲取 Call 適配器
CallAdapter<ResponseT, ReturnT> callAdapter = createCallAdapter(retrofit, method);
Type responseType = callAdapter.responseType();
// 2. 獲取響應數據轉換器
Converter<ResponseBody, ResponseT> responseConverter = createResponseConverter(retrofit, method, responseType);
okhttp3.Call.Factory callFactory = retrofit.callFactory;
// 3. 根據解析的參數創建 HttpServiceMethod
return new HttpServiceMethod<>(requestFactory, callFactory, callAdapter, responseConverter);
}
HttpServiceMethod#invok
執行時源碼如下:
@Override ReturnT invoke(Object[] args) {
// 創建一個 OkHttpCall, 用於進行網絡請求和響應數據轉換
return callAdapter.adapt(
new OkHttpCall<>(requestFactory, args, callFactory, responseConverter));
}
至此,便是一個服務接口從解析到創建成一個OkHttp#Call
的過程,縱觀全局,其實這個過程就好比一個爲了將如下接口:
public interface Service{
@GET("article/list/{page}/json")
public Call<ResponseEntry<ResponseData>> getHomeList(@Path("page")int page);
}
解析成一個請求鏈接爲http://wanandroid.com/article/list/0/json
,請求方式爲 GET
,請求的調用方式爲:
Service service = ...;
// 相當於執行 HttpServiceMethod#invoke 方法
Call<ResponseEntry<ResponseData>> = service.getHomeList(0);
的過程,而這個過程中需要解決將接口轉換成對象實例、將方法註解、參數解析處理拼接爲請求連接、最後確定返回類型的問題,此時Call
尚未進行請求;
3. Call
請求執行到響應數據回調過程分析
關於OkHttp#Call
如何運作的問題已經在 OkHttp源碼解析 一文中做了詳細分析,這裏的不同之處在於,在Retrofit
中我們需要更多地關注它是如何協調請求和響應,最終回調給UI
線程的。
OK,從HttpServiceMethod#invoke
出發,根據前面的內容中我們已經知道它會通過callAdapter.adapt(new OkHttpCall<>(requestFactory, args, callFactory, responseConverter))
返回一個Call
實例,並且在Android
平臺上會將響應數據回調在UI
線程的Handler
上,因此我們先關注一下Android
平臺下的默認CallAdapter
,於是定位到Android#defaultCallAdapterFactories
:
@Override List<? extends CallAdapter.Factory> defaultCallAdapterFactories(
@Nullable Executor callbackExecutor) {
if (callbackExecutor == null) throw new AssertionError();
return singletonList(new ExecutorCallAdapterFactory(callbackExecutor));
}
可見Android
平臺下的默認CallAdapter
是ExecutorCallAdapterFactory
, 於是可以定位到ExecutorCallAdapterFactory#adapt
:
@Override public Call<Object> adapt(Call<Object> call) {
return new ExecutorCallbackCall<>(callbackExecutor, call);
}
ExecutorCallbackCall
這裏實際是使用了裝飾器模式,它將工作委託給了callbackExecutor
和delegate
,而它自身僅僅起到了協調作用,將響應數據回調到UI
線程:
static final class ExecutorCallbackCall<T> implements Call<T> {
final Executor callbackExecutor;
final Call<T> delegate;
ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) {
this.callbackExecutor = callbackExecutor;
this.delegate = delegate;
}
@Override public void enqueue(final Callback<T> callback) {
checkNotNull(callback, "callback == null");
delegate.enqueue(new Callback<T>() {
@Override public void onResponse(Call<T> call, final Response<T> response) {
// 回調至主線程
callbackExecutor.execute(new Runnable() {
@Override public void run() {
if (delegate.isCanceled()) {
callback.onFailure(ExecutorCallbackCall.this, new IOException("Canceled"));
} else {
callback.onResponse(ExecutorCallbackCall.this, response);
}
}
});
} // ...
});
} // ...
}
具體到網絡請求的執行與響應數據的轉換工作還得看OkHttpCall
,這裏我們只關注一下OKHttpCall#enqueue
即可, 可見這裏除了請求網絡數據外,還會先轉換響應數據後再回調給上一級:
@Override public void enqueue(final Callback<T> callback) {
okhttp3.Call call;
Throwable failure;
// 1. 執行請求
call.enqueue(new okhttp3.Callback() {
@Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
Response<T> response;
try {
// 2. 解析響應數據,將網絡響應數據轉換成指定數據類型
response = parseResponse(rawResponse);
} catch (Throwable e) {
// ...
return;
}
try {
// 3. 將解析完成的數據回調給上一級
callback.onResponse(OkHttpCall.this, response);
} catch (Throwable t) {
// ...
}
}
// ...
});
}
然後parseResponse
部分源碼如下,可見這裏會通過Converter
網絡響應數據轉換爲我們指定的數據類型:
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();
// ...
ExceptionCatchingResponseBody catchingBody = new ExceptionCatchingResponseBody(rawBody);
try {
// 通過轉換器轉換數據
T body = responseConverter.convert(catchingBody);
return Response.success(body, rawResponse);
} catch (RuntimeException e) {
// ...
}
}
綜上可知,最終網絡請求會在OkHttpCall
中執行,獲取響應數據後通過設定的Converter
轉換器將數據轉換成指定類型;而最終回調給UI
線程則是在ExecutorCallbackCall
中進行,作爲裝飾器,它實際將請求和響應數據處理工作都委託給了OkHttpCall
,而自身僅僅做了最終數據的回調處理。
於是整體執行流程如下:
三、Proxy
這裏指的是反射工具類中的java.lang.reflect.Proxy
,通過前面的分析,我們已經知道,我們建立的服務接口會通過Proxy.newProxyInstance
來實例化一個代理對象實例,而通過這個實例化的對象,就能像使用普通類對象實例一個調用方法。
這裏我比較好奇的是它是如何給接口實例化的,因此咱就來研究研究,定位到Proxy#newProxyInstance
,精簡一下源碼(去除了驗證邏輯等),如下,可以發現Proxy
會爲我們的服務接口構建一個代理類(當然會先從代理類緩存,也就是WeakCache
中查找已經構建的代理類),然後通過這個類的構造函數構建出一個實例對象出來:
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
final Class<?>[] intfs = interfaces.clone();
// 1. 從 `WeakCache`中查找,或者創建一個接口的代理類
Class<?> cl = getProxyClass0(loader, intfs);
// 2. 拿到代理類的構造函數
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
// ...
// 3. 通過構造函數創建一個實例對象
return cons.newInstance(new Object[]{h});
}
再來看看getProxyClass0()
, 根據代碼註釋可知,如果根據類加載器查找已經實現的代理類,那麼直接返回拷貝的緩存,如果沒找到,那麼就會通過ProxyClassFactory
去創建一個代理類。
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
return proxyClassCache.get(loader, interfaces);
}
於是再來看看ProxyClassFactory
,可知通過其apply
方法會根據我們服務接口的信息配置代理類,然後通過ProxyGenerator
生成一個代理類class
文件,最終通過defineClass0
將這個代理類定義出來:
private static final class ProxyClassFactory
implements BiFunction<ClassLoader, Class<?>[], Class<?>>
{
private static final String proxyClassNamePrefix = "$Proxy";
@Override
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
// ...
int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
// 1. 進行一系列的代理類信息的配置
//...
// 2. 根據配置信息生成代理類class文件
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);
// 3. 最終生成特定代理類
return defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length);
}
}
這裏的defineClass0
是個native
方法,因此就不再深挖了:
private static native Class<?> defineClass0(ClassLoader loader, String name, byte[] b, int off, int len);
至此,是不是已經明白了Proxy
如何實例化接口的呢?
四、總結
通過上面的分析,可以發現 Retrofit
更像是對一個OkHttp
請求的抽取與封裝:
- 網絡請求參數全部抽離成服務接口方法的註解,註解參數解析和
Request
構建工作抽離到了RequestFactory
。 -
CallAdapter
將OkHttpCall
的執行匹配到我們指定的執行器,而Converter
則將網絡響應數據轉換成我們想要的類型 - 最終,在
Android
平臺上直接將指定的數據類型返回給UI
線程的Handler
處理。
關於Proxy
,它將服務接口轉換成一個代理類對象實例的實現方式也很值得我們學習。