最近看了一下Retrofit2的源碼,感覺並不是很難,但是對於其中的設計方式大爲讚歎,怪不得會成爲現在最流行的網絡請求框架,Retrofit感覺就像是一個包裝殼,我不幹請求這種髒話,還是你okhttp幹吧,畢竟你做得好,在內部來說我就是專門給你檢查檢查一些url接口的規範,或是是一些Request,你就專心幹你的請求就行,對於外部使用者來說,我什麼都給你封裝好了,你要啥我就給你啥,你想要什麼類型的結果數據我就給你什麼樣的數據,你不要管我幹了什麼,這種不管不問的方式,我還是挺喜歡的。
下面貼出Retrofit的基本使用,然後跟着這個基本結構去分析源碼,這樣,有利於去探索。
public interface APIServer {
@GET("/")
Call<ResponseBody> getText();
}
Retrofit rv = new Retrofit.Builder()
.baseUrl("http://www.lontano.wang/")
.build();
APIServer apiServer = rv.create(APIServer.class);
Call<ResponseBody> call = apiServer.getText();
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
response.toString()
}
@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
}
});
這是最基本的請求步驟了,接下來一步步的分析。
初始化
我們來看看初始化
Retrofit rv = new Retrofit.Builder()
.baseUrl("http://www.lontano.wang/")
.build();
先看看內部建造者Builder類的初始化,下面有一個Platform類,這個類是用來獲取當前的使用平臺,有默認值,java8平臺,還有Android平臺。最下面默認還添加了一個BuiltInConverters,這個是默認的覆蓋結果的類,我們常常在請求返回結果類型的時候希望返回一個處理好的bean,所以,我們使用Retrofit的時候喜歡用GsonConverterFactory.create(),如果使用者沒有添加(就像我上面的例子)的話就是用默認值,這個類覆蓋結果的是一個String類型的結果,所有覆蓋結果類都繼承了Retrofit的Converter.Factory類,並對結果進行轉換。
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());
}
接下來看看baseUrl方法,這個方法主要用來檢查這個鏈接是否正確,然後是區分是http還是https,然後並對這個鏈接進行再一次拼接。主要操作都在HttpUrl這個類裏面
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);
}
然後看build方法,這個方法主要就是對剛剛設置build參數做最後的總結,我們來看看這個callFactory,因爲Retrofit把okHttp都封裝好了,我們根本無法對他進行設置,比如,我要用session去請求,而且我還要設置請求頭或是請求時間,但是我接觸不到okHttp啊,所以,這個時候,我們可以通過Build的 public Builder client(OkHttpClient client) 將需要設置okHttp的設置帶進來,下面的這個callFactory如果爲null的話,就默認初始化一個,
public Retrofit build() {
if (baseUrl == null) {
throw new IllegalStateException("Base URL required.");
}
//-------callFactory
okhttp3.Call.Factory callFactory = this.callFactory;
if (callFactory == null) {
callFactory = new OkHttpClient();
}
//is not used for addCallAdapterFactory custom method return types
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));
/*返回類型添加到集合中,這個地方比較常見的就是RxJava,可以通過build方法的
addCallAdapterFactory(RxJava2CallAdapterFactory.create())將返回類型進行轉換
*/
// Make a defensive copy of the converters.
List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories);
//返回給Retrofit類
return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
callbackExecutor, validateEagerly);
}
}
使用
好了,Build的過程結束了,這個返回的new Retrofit也無處看了,也都是剛纔的值,設置到Retrofit的全局去了,恩,我們接下來直接看使用方法了,來看看
APIServer apiServer = rv.create(APIServer.class);
這個類纔是最核心的地方,我們點進去看看,這個類使用了動態代理,在原來調用的方法中,添加一些自己的處理,這個動態代理主要乾了些什麼呢?
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, @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);
}
//這個處理也不看,只有平臺是java8的時候纔有用
if (platform.isDefaultMethod(method)) {
return platform.invokeDefaultMethod(method, service, proxy, args);
}
//這裏是重點
ServiceMethod<Object, Object> serviceMethod =
(ServiceMethod<Object, Object>) loadServiceMethod(method);
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
}
});
}
我們來探探上面註釋“重點”的地方,我們一個一個單獨拿出來說首先看ServiceMethod方法,這個方法主要功能就是通過註解解析調用方法一些請求信息,將這些信息緩存起來,下次調用的時候直接拿,這個方法調用了loadServiceMethod來返回一個ServiceMethod,我們來看看這個方法。
ServiceMethod<Object, Object> serviceMethod =
(ServiceMethod<Object, Object>) loadServiceMethod(method);
首先是一個判斷當前緩存是否有該方法,沒有的話就去初始化,初始化類用的是ServiceMethod.Builder去構建的,並將Retrofit引用和方法信息傳遞過去。
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;
}
我們來看看ServiceMethod.Builder的build方法幹了些什麼,因爲代碼過長,我把主要的代碼拎出來。
/*首先創建一個CallAdapter,這個類主要檢查一些註解的規範,比如方法的修飾不能爲void,必須要有返回值,然後獲取該方法的註解信息,
Retrofit繼續調用 return nextCallAdapter(null, returnType, annotations)方法,
該方法通過遍歷類型CallAdapter.Factory的集合獲取對應的CallAdapter,對於這個CallAdapter我們有一個非常常見的使用,那就是
addCallAdapterFactory(RxJava2CallAdapterFactory.create()),
我們在使用RxJava來作爲返回類型的時候,就是這樣爲Retrofit來添加一個返回類型的,所以,
此處如果開發者使用了RxJava類返回類型的話,那麼這個地方所創建的返回類型就是RxJava返回的類型,
*/
callAdapter = createCallAdapter();
//此處去獲取CallAdapter的響應類型,這個responseType還使用了檢測,不能使用Response.class ,
responseType = callAdapter.responseType();
//獲取方法的註解和返回類型,返回給Retrofit的 return retrofit.responseBodyConverter(responseType, annotations);
//然後返回return (Converter<ResponseBody, T>) converter;
//這個地方比較常見的就是addConverterFactory(GsonConverterFactory.create())添加覆蓋的返回類型爲Gson
responseConverter = createResponseConverter();
//這個parseMethodAnnotation方法應該是大家最常用的了,這個方法是獲取請求方法的註解,
//Retrofit支持的請求有DELETE、GET、HEAD、PATCH、POST、PUT、OPTIONS等等,通過instanceof來判斷當前的類型,
//然後調用parseHttpMethodAndPath來檢查註解的參數,並賦給全局,這個地方有個檢查,當使用Multipart註解的時候,必須是PATCH、POST、PUT、OPTIONS來修飾,
//如果使用GET請求的話,不能用他來修飾,和他一樣的還是FormUrlEncoded,這兩個在平時使用的時候,都是和POST一塊使用
for (Annotation annotation : methodAnnotations) {
parseMethodAnnotation(annotation);
}
//
for (int p = 0; p < parameterCount; p++) {
//獲取參數類型
Type parameterType = parameterTypes[p];
//獲取參數的註解
Annotation[] parameterAnnotations = parameterAnnotationsArray[p];
//創建ParameterHandler對象來存放檢查過後的參數和註解,這個方法中會對
//Path、Query、QueryName、QueryMap、Header、HeaderMap、Field、FieldMap、Part、PartMap、Body來進行一些規範的檢查
parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);
}
最後將當前的引用信息返回ServiceMethod類return new ServiceMethod<>(this);
好了,最關鍵的ServiceMethod終於結束了,果然是重中之中,然後我們返回到Retrofit的代理中繼續接下來的操作。
//這個類封裝了OkHttp的enqueue和execute,執行enqueue方法會調用okhttpenqueue方法,
//通過回調的方式,將okHttp請求的結果回調給Retrofit,然後回調給用戶
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
//返回callAdapter的類型,如果添加了RxJava2CallAdapterFactory,
//那麼就返回RxJava類型,然後就可以通過subscribe訂閱的方式實現鏈式調用
return serviceMethod.callAdapter.adapt(okHttpCall);
至此,Retrofit的源碼大致分析如此,最後來個整體的總結。
總結
1.通過Retrofit.Builder()構建baseUrl,類型的覆蓋(addConverterFactory),請求結果轉換(addCallAdapterFactory)
2. 通過create(APIServer.class)實現動態代理APIServer類的處理
3. apiServer.getText()動態代理方法的執行
4. 處理ServiceMethod,並對一些註解進行規範化檢查,將結果緩存起來
5. 創建OkHttpCall通過okHttp去處理請求結果
6. serviceMethod.callAdapter.adapt返回結果類型
附錄
最後附上compile庫,以後添加的時候好用一些,RxAndroid、OkHttp,RxJava,Gson,RxBinding,Rxpermissions全家桶
compile 'io.reactivex.rxjava2:rxjava:2.0.0'
compile 'io.reactivex.rxjava2:rxandroid:2.0.0'
compile 'org.reactivestreams:reactive-streams:1.0.0'
compile 'com.squareup.okhttp3:okhttp:3.4.1'
compile 'com.squareup.okio:okio:1.10.0'
compile 'com.squareup.retrofit2:retrofit:2.1.0'
compile 'com.jakewharton.retrofit:retrofit2-rxjava2-adapter:1.0.0'
compile 'com.squareup.retrofit2:converter-gson:latest.release'
compile 'com.jakewharton.rxbinding2:rxbinding:2.0.0'
compile 'com.jakewharton.rxbinding2:rxbinding-support-v4:2.0.0'
compile 'com.jakewharton.rxbinding2:rxbinding-appcompat-v7:2.0.0'
compile 'com.jakewharton.rxbinding2:rxbinding-design:2.0.0'
compile 'com.jakewharton.rxbinding2:rxbinding-recyclerview-v7:2.0.0'
compile 'com.jakewharton.rxbinding2:rxbinding-leanback-v17:2.0.0'
compile 'com.tbruyelle.rxpermissions2:rxpermissions:0.9.3@aar'