在Android客戶端的項目網(wǎng)絡(luò)請求實踐中,對retrofit進行了實踐和源碼的閱讀。從retrofit的用法入手,對retrofit進行解析。
首先看一下retrofit的基本用法:
第一步創(chuàng)建retrofit對象:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(baseUrl)
.build();
第二步創(chuàng)建一個service接口,接口的組成如下
public interface NetWorkService {
@FormUrlEncoded
@Post("/reativePath")
Call<ResponseBody> getResponse(@Field("param") String param);
}
第三進行網(wǎng)絡(luò)請求
NetWorkService service = retrofit.create(NetWorkService.class);
Call<ResponseBody> call = service.getResponse(param);
查看Retrofit的類結(jié)構(gòu),使用了經(jīng)典的Builder設(shè)計模式,通過Builder的方法我們通??梢栽O(shè)置OkHttpClient、HttpUrl、ConverterFactory、CallAdapterFactory等。
查看Retrofit的create方法
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<Object, Object> serviceMethod =
(ServiceMethod<Object, Object>) loadServiceMethod(method);
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
}
});
這里使用了Java的動態(tài)代理模式,關(guān)于代理模式和動態(tài)代理,可以查看這里。
在create中,創(chuàng)建了一個最終暴露給我們的代理類A,類型為T,也就是xxService,內(nèi)部的委托類B為service,也是我們的類xxService。其中起到2個類的橋接作用的是一個實現(xiàn)了InvocationHandler接口的匿名類C,C為A的委托類,為B的代理類。最終A在外部對接口方法的調(diào)用會傳遞到C中,C再將調(diào)用傳遞給B。
在匿名類中
ServiceMethod<Object, Object> serviceMethod =
(ServiceMethod<Object, Object>) loadServiceMethod(method);
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
這一段代碼就是在代理類中進行的調(diào)用。我們進入這幾個方法可以看到retrofit對于xxService的整個處理。
查看loadService的代碼
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);
}
}
在Retrofit類中維護了一個Map用來存放ServiceMethod,如果method對象已經(jīng)存在就會使用已經(jīng)存在的緩存對象,避免每次對象在代理類里面運行的開銷。
進入ServiceMethod的build方法,我們可以看到retrofit對service的處理。我將service接口的處理過程梳理了一下
- 創(chuàng)建CallAdapter
- 創(chuàng)建處理返回數(shù)據(jù)格式的Converter
- 解析方法上方的注解。例如@FormUrlEncoded、@Post
- 新建參數(shù)處理對象ParamterHandler
- 對service接口中的方法參數(shù)進行讀取和解析,在這里,會對參數(shù)注解進行解析。
接下來我們對每一個步驟進行解析
CallAdapter
service的build中會調(diào)用retrofit的callAdapter方法,繼而調(diào)用nextCallAdapter方法。
int start = adapterFactories.indexOf(skipPast) + 1;
for (int i = start, count = adapterFactories.size(); i < count; i++) {
CallAdapter<?, ?> adapter = adapterFactories.get(i).get(returnType, annotations, this);
if (adapter != null) {
return adapter;
}
}
我們會取出retrofit的存放adapter的list中第一個不為空的adapter作為最后使用的adapter,并且根據(jù)returntype去除CallAdapter,結(jié)合動態(tài)代理部分可以看出,retrofit會把Call根據(jù)我們的CallAdapter轉(zhuǎn)換為想要的類型。例如retrofit+rxjava使用的時候把Call轉(zhuǎn)化為一個Observable。
Converter
接下來會執(zhí)行createResponseConverter方法,這個主要是解析返回的數(shù)據(jù)。在里面會調(diào)用retrofit的responseBodyConverter方法,類似callAdapter,接下來會調(diào)用nextResponseBodyConverter方法
int start = converterFactories.indexOf(skipPast) + 1;
for (int i = start, count = converterFactories.size(); i < count; i++) {
Converter<ResponseBody, ?> converter =
converterFactories.get(i).responseBodyConverter(type, annotations, this);
if (converter != null) {
//noinspection unchecked
return (Converter<ResponseBody, T>) converter;
}
}
我們會拿到retrofit中存放converter的list中第一個獲取到ConVerter對象不為空的作為方法的返回值。例如GsonResponseBodyConverter中就會把返回的response數(shù)據(jù)利用Gson解析為對應(yīng)的實體類對象。
而且在retrofit的Build類的build方法中,在存adapter的list里面加入了默認(rèn)的對象
adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));
默認(rèn)添加的是一個DefaultCallAdapterFactory對象
而converter的list則會默認(rèn)添加一個BuiltInConverters對象
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());
}
parseMethodAnnotation
接下來會對retrofit的xxService接口進行方法注解的解析
截取一小段代碼進行分析:
if (annotation instanceof POST) {
parseHttpMethodAndPath("POST", ((POST) annotation).value(), true);
}
其中最后一個參數(shù)的true和false是methodService中的hasBody變量
根據(jù)源碼我們可以將方法注解分類如下:
- Http方法 hasBody為false
- delete
- get
- head
- options
- Http方法 hasBody為true
- patch
- post
- put
- 非http請求
- Http
- Headers
- FormUrlEncoded
- Multipart
其中FormUrlEncoded表示參數(shù)編碼為表單,multipart為文件上傳。2者互斥不得同時存在。
接下來會對方法參數(shù)的路徑進行解析以及驗證
如果方法注解為headers,則表示里面為http頭信息,會單獨進行解析。
ParameterHandler
創(chuàng)建ParamterHandler對象,這個對象是用來處理參數(shù)的。在后面的request構(gòu)建會使用到。其中,每一個method的注解都有它對應(yīng)的ParamterHandler對象。
parseParameter
在serviceMethod對象build前的最后一步是解析method的參數(shù)注解,會將讀取到的數(shù)據(jù)傳入ParamterHandler對象中,最終拼成http請求的參數(shù)
最后,在Retrofit的create中,我們會返回一個Call對象,這個對象實際在默認(rèn)情況下是一個ExecutorCallbackCall對象,ExecutorCallbackCall對象內(nèi)部包裹了一個OkHttpCall對象。
OkHttpCall
OkHttpCall對象是實際上執(zhí)行網(wǎng)絡(luò)請求的類,這個對象實際是包裝了OkHttp里面的Call對象。最終在enqueue等方法中會調(diào)用OkHttp的對應(yīng)的方法。
小結(jié)
陸陸續(xù)續(xù)看了一段時間的retrofit源碼,雖然這個庫的代碼不多,但是設(shè)計思路非常的牛逼。我們應(yīng)該多吸收這種優(yōu)秀三方庫的思想到我們自己的代碼中