文章使用的Retrofit版本爲:2.3.0
在前面的文章已經介紹過了Retrofit的使用,這篇文章主要是分析Retrofit的源碼。
源碼解析
首先我們從Retrofit的初始化配置開始說起:
Retrofit retrofit = new Retrofit.Builder()
.client(new OkHttpClient.Builder().build())
.baseUrl("http://www.baidu.com/")
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
這是我們常見的Retrofit初始化的代碼,從 Builder()
、.build()
就能明白Retrofit和okhttp一樣是使用建造者模式來構建對象的,看源碼我們先看這個類的成員變量,好的代碼規範從命名我們就能初步理解這個類大概是做哪些事:
public static final class Builder {
private final Platform platform; // 運行的平臺,Retrofit可以運行在ios、Android、Java3個平臺環境下,會根據當前環境去適配不同的平臺
@Nullable
private Factory callFactory;// Okhttp的工廠類
private HttpUrl baseUrl;// baseUrl,HttpUrl類型
private final List<retrofit2.Converter.Factory> converterFactories; // 數據轉換器的集合,主要作用是將網絡請求返回的數據轉換成用戶想要的格式,用戶可自定義解析方式,比如Gson、FastJson
private final List<retrofit2.CallAdapter.Factory> adapterFactories; // 數據適配器的集合,主要作用是將接口返回的對象適配成用戶想要的對象,用戶可自定義,比如RxJava
@Nullable
private Executor callbackExecutor;// 回調執行器,網絡請求返回數據通過這個進行回調
private boolean validateEagerly;// 是否提前解析註解等信息
//省略其餘代碼...
}
代碼中的註釋是我自己加上去的,註釋很詳細,這裏就不多解釋了。
我們跟着 .build()
方法進去看源碼:
public Retrofit build() {
if (this.baseUrl == null) {
throw new IllegalStateException("Base URL required.");
} else {
Factory callFactory = this.callFactory;
if (callFactory == null) {
callFactory = new OkHttpClient();
}
Executor callbackExecutor = this.callbackExecutor;
if (callbackExecutor == null) {
callbackExecutor = this.platform.defaultCallbackExecutor();
}
List<retrofit2.CallAdapter.Factory> adapterFactories = new ArrayList(this.adapterFactories);
adapterFactories.add(this.platform.defaultCallAdapterFactory(callbackExecutor));
List<retrofit2.Converter.Factory> converterFactories = new ArrayList(this.converterFactories);
return new Retrofit((Factory)callFactory, this.baseUrl, converterFactories, adapterFactories, callbackExecutor, this.validateEagerly);
}
}
build()
方法中首先就判斷了baseUrl是否爲空,如果爲空會拋出一個異常提示 Base URL required.
這也是爲什麼在 Retrofit 2.x 系列之一 - Retrofit的使用姿勢(基礎篇) 這篇文章中說 baseUrl()
爲必需項了。
當baseUrl不爲空時,走到else的代碼分支,判斷 this.callFactory
是否爲空,如果爲空則初始化一個默認的 OkhttpClient
對象,同樣也驗證了基礎篇文章中說的 client()
爲可選項,這一步我們先停下來看 this.callFactory
在哪裏被賦值的:
public Retrofit.Builder client(OkHttpClient client) {
return this.callFactory((Factory)Utils.checkNotNull(client, "client == null"));
}
public Retrofit.Builder callFactory(Factory factory) {
this.callFactory = (Factory)Utils.checkNotNull(factory, "factory == null");
return this;
}
通過 Builder().client()
方法傳入 OkhttpClient
實例,緊接着調用自己的 callFactory()
方法,在該方法內部對 callFactory
這個成員變量賦值。我們回到 build()
方法繼續看。
判斷完 callFactory
之後,判斷 callbackExecutor
,這個變量我們一般都不會去配置它,所以這裏默認爲null,Retrofit就會使用各自平臺的 defaultCallbackExecutor
。因爲我們是 Android
中使用,我們去看Android平臺默認的 callbackExecutor
是什麼:
static class Android extends Platform {
Android() {
}
public Executor defaultCallbackExecutor() {
return new Platform.Android.MainThreadExecutor();
}
Factory defaultCallAdapterFactory(@Nullable Executor callbackExecutor) {
if (callbackExecutor == null) {
throw new AssertionError();
} else {
return new ExecutorCallAdapterFactory(callbackExecutor);
}
}
static class MainThreadExecutor implements Executor {
private final Handler handler = new Handler(Looper.getMainLooper());
MainThreadExecutor() {
}
public void execute(Runnable r) {
this.handler.post(r);
}
}
}
代碼中可以看到無參的 defaultCallbackExecutor()
方法中返回的是 MainThreadExecutor
,也就是將返回接口切換到主線程中執行。
最後 build()
方法創建了 adapterFactories
、converterFactories
的集合,並將我們添加進去的數據轉換器(本文中爲 GsonConverterFactory
)、數據適配器(本文中爲 RxJavaCallAdapterFactory
)傳入集合,然後使用配置好的所有參數構建了Retrofit對象。至此Retrofit就創建好了。
初始化配置完成後,第二步就是與網絡請求的接口類配合,創建能夠發起網絡請求的對象。但是在Retrofit的使用步驟中,我們是創建了一個接口來配置網絡請求的方式、參數、Header等等,接口是不能實例化的,所以,Retrofit使用了動態代理
的方式(非常重要),動態代理的相關知識可以看:你真的完全瞭解Java動態代理嗎?看這篇就夠了 這篇文章,裏面描述了動態代理的使用場景、設計思想和原理。我們看一下Retrofit是如何使用動態代理的:
Api api = retrofit.create(Api.class);
我們跟着 create()
方法進入源碼:
public <T> T create(final Class<T> service) {
Utils.validateServiceInterface(service);
if (this.validateEagerly) {
this.eagerlyValidateMethods(service);
}
return Proxy.newProxyInstance(service.getClassLoader(), new Class[]{service}, new InvocationHandler() {
private final Platform platform = Platform.get();
public Object invoke(Object proxy, Method method, @Nullable Object[] args) throws Throwable {
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
} else if (this.platform.isDefaultMethod(method)) {
return this.platform.invokeDefaultMethod(method, service, proxy, args);
} else {
ServiceMethod<Object, Object> serviceMethod = Retrofit.this.loadServiceMethod(method);
OkHttpCall<Object> okHttpCall = new OkHttpCall(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
}
}
});
}
create()
方法中第一步先驗證一下傳遞進來的接口類的合法性,第二步判斷是否要提前解析接口類中的方法,第三步就是創建動態代理了。這裏的動態代理會將我們編寫的請求接口類中所有方法,都委託到這個動態代理去處理,每當我們調用一個方法,動態代理中 InvocationHandler
對象的 invoke
方法就會執行一次。 invoke
方法中前兩個 if
代碼塊中都是別的平臺(ios、java8),我們只要關注最後的 else
代碼塊,裏面的三行代碼就是Retrofit最重要的地方了,我們跟着第一個方法 loadServiceMethod
進去看:
ServiceMethod<?, ?> loadServiceMethod(Method method) {
ServiceMethod<?, ?> result = (ServiceMethod)this.serviceMethodCache.get(method);
if (result != null) {
return result;
} else {
Map var3 = this.serviceMethodCache;
synchronized(this.serviceMethodCache) {
result = (ServiceMethod)this.serviceMethodCache.get(method);
if (result == null) {
result = (new retrofit2.ServiceMethod.Builder(this, method)).build();
this.serviceMethodCache.put(method, result);
}
return result;
}
}
}
該方法會先從緩存中獲取 ServiceMethod
對象,如果獲取不到就利用 Builder
創建一個,再加入緩存。我們看看 ServiceMethod.Builder
對象是什麼,因爲 Builder
是用來創建 ServiceMethod
的,知道了 Builder
就能知道 ServiceMethod
,和查看 Retrofit
類一樣,先看下成員變量都有什麼:
static final class Builder<T, R> {
final Retrofit retrofit;
final Method method;
final Annotation[] methodAnnotations; // 方法的註解:@Post、@Header 等等
final Annotation[][] parameterAnnotationsArray; // 方法中參數的註解:@Field、@Query 等等
final Type[] parameterTypes; //參數類型
Type responseType; //返回類型
boolean gotField;
boolean gotPart;
boolean gotBody;
boolean gotPath;
boolean gotQuery;
boolean gotUrl;
String httpMethod; // 請求方法
boolean hasBody; // 是否有請求體
boolean isFormEncoded; // 是否是表單
boolean isMultipart; // 是否是文件
String relativeUrl;
Headers headers; // 請求頭
MediaType contentType; // 媒體類型
Set<String> relativeUrlParamNames;
ParameterHandler<?>[] parameterHandlers; // 根據請求方法將參數中的註解處理成網絡請求的請求體內容
Converter<ResponseBody, T> responseConverter; //返回數據的轉換器
CallAdapter<T, R> callAdapter; // 方法返回值得對象適配器
//省略其餘代碼...
}
代碼中添加了註釋,通過註釋我們能知道,這個 ServiceMethod
類就是對我們自定義的 ApiService接口
中的所有註解、方法、參數逐一去進行解析,解析的步驟在該類的 build()
方法中:
public ServiceMethod build() {
this.callAdapter = this.createCallAdapter();
this.responseType = this.callAdapter.responseType();
if (this.responseType != Response.class && this.responseType != okhttp3.Response.class) {
this.responseConverter = this.createResponseConverter();
Annotation[] var1 = this.methodAnnotations;
int p = var1.length;
for(int var3 = 0; var3 < p; ++var3) {
Annotation annotation = var1[var3];
this.parseMethodAnnotation(annotation);
}
if (this.httpMethod == null) {
throw this.methodError("HTTP method annotation is required (e.g., @GET, @POST, etc.).");
} else {
if (!this.hasBody) {
if (this.isMultipart) {
throw this.methodError("Multipart can only be specified on HTTP methods with request body (e.g., @POST).");
}
if (this.isFormEncoded) {
throw this.methodError("FormUrlEncoded can only be specified on HTTP methods with request body (e.g., @POST).");
}
}
int parameterCount = this.parameterAnnotationsArray.length;
this.parameterHandlers = new ParameterHandler[parameterCount];
for(p = 0; p < parameterCount; ++p) {
Type parameterType = this.parameterTypes[p];
if (Utils.hasUnresolvableType(parameterType)) {
throw this.parameterError(p, "Parameter type must not include a type variable or wildcard: %s", parameterType);
}
Annotation[] parameterAnnotations = this.parameterAnnotationsArray[p];
if (parameterAnnotations == null) {
throw this.parameterError(p, "No Retrofit annotation found.");
}
this.parameterHandlers[p] = this.parseParameter(p, parameterType, parameterAnnotations);
}
if (this.relativeUrl == null && !this.gotUrl) {
throw this.methodError("Missing either @%s URL or @Url parameter.", this.httpMethod);
} else if (!this.isFormEncoded && !this.isMultipart && !this.hasBody && this.gotBody) {
throw this.methodError("Non-body HTTP method cannot contain @Body.");
} else if (this.isFormEncoded && !this.gotField) {
throw this.methodError("Form-encoded method must contain at least one @Field.");
} else if (this.isMultipart && !this.gotPart) {
throw this.methodError("Multipart method must contain at least one @Part.");
} else {
return new ServiceMethod(this);
}
}
} else {
throw this.methodError("'" + Utils.getRawType(this.responseType).getName() + "' is not a valid response body type. Did you mean ResponseBody?");
}
}
第一個for循環中的 this.parseMethodAnnotation(annotation);
主要是對請求方法(@GET、@POST、@DELETE等等)進行解析。
然後我們回到創建動態代理的 invoke()
方法源碼中:
ServiceMethod<Object, Object> serviceMethod = Retrofit.this.loadServiceMethod(method);
OkHttpCall<Object> okHttpCall = new OkHttpCall(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
通過 loadServiceMethod()
方法已經創建好 ServiceMethod
,而 ServiceMethod
類中也保存瞭解析後的請求所需的各種相關信息,緊接着創建了 OkHttpCall
對象,這個對象是 Retrofit
中對 OkHttp
的 Call
接口的實現類,當我們調用 異步請求 enqueue
或者 同步請求 execute
時,就是通過 OkHttpCall
去調用 OkHttp
的請求,所以我們說 Retrofit
和其他 OkHttpUtils
的開源項目一樣,只是對 OkHttp
的一種封裝,底層的網絡請求還是通過 OkHttp
發起的。由於篇幅過長, OkHttpCall
源碼相對簡單,我們這裏就不展開對它的源碼解析了,感興趣的道友可自行查看。
回到主題,我們接着看最後一行代碼,調用了 serviceMethod.callAdapter.adapt(okHttpCall);
,這裏就是爲什麼我們通過添加 RxJavaCallAdapterFactory
之後,我們可以在請求結果返回 RxJava
的對象了。
到這裏我們還存在一個疑問,RxJavaCallAdapterFactory
的工作時機我們看到了,那 GsonConverterFactory
呢?
這就要說回 OkhttpCall
了,假設我們執行的是 異步請求 enqueue
,在其內部最終會調用 OkHttp
的 call
的 enqueue
方法:
call.enqueue(new okhttp3.Callback() {
public void onResponse(okhttp3.Call call, Response rawResponse) throws IOException {
retrofit2.Response response;
try {
response = OkHttpCall.this.parseResponse(rawResponse);
} catch (Throwable var5) {
this.callFailure(var5);
return;
}
this.callSuccess(response);
}
//省略其他代碼...
});
在結果的回調中,調用了自己的 parseResponse
方法:
retrofit2.Response<T> parseResponse(Response rawResponse) throws IOException {
ResponseBody rawBody = rawResponse.body();
rawResponse = rawResponse.newBuilder().body(new OkHttpCall.NoContentResponseBody(rawBody.contentType(), rawBody.contentLength())).build();
int code = rawResponse.code();
if (code >= 200 && code < 300) {
if (code != 204 && code != 205) {
OkHttpCall.ExceptionCatchingRequestBody catchingBody = new OkHttpCall.ExceptionCatchingRequestBody(rawBody);
try {
T body = this.serviceMethod.toResponse(catchingBody);
return retrofit2.Response.success(body, rawResponse);
} catch (RuntimeException var9) {
catchingBody.throwIfCaught();
throw var9;
}
} else {
rawBody.close();
return retrofit2.Response.success((Object)null, rawResponse);
}
} else {
retrofit2.Response var5;
try {
ResponseBody bufferedBody = Utils.buffer(rawBody);
var5 = retrofit2.Response.error(bufferedBody, rawResponse);
} finally {
rawBody.close();
}
return var5;
}
}
在一個try catch代碼塊中,調用了 this.serviceMethod.toResponse(catchingBody);
這裏就是去找到我們創建 Retrofit
時傳遞進去的 ConverterFactory
把網絡請求返回的數據轉換成我們自己想要的格式。
至此,整個 Retrofit
的源碼就解析完了,在整個過程中我們會發現 Retrofit
將設計模式充分運用在了整個項目中,比如:Builder模式(建造者模式)、適配器模式(Adapter)、工廠模式(Factory)、代理模式(Procy)、裝飾模式(OkHttpCall)等等,這也讓我意識到設計模式能夠給我們的架構帶來質的飛躍,因此還需要多看源碼,才能不斷進步。