本次分析的要點:
- Retrofit中的動態(tài)代理和整個流程(Proxy诊沪、ServiceMethod、OkHttpCall)
- Retrofit中的轉(zhuǎn)換器和適配器(Converter默刚、CallAdapter)
Retrofit整體流程和動態(tài)代理
首先我們回顧一下retrofit的使用demo
Retrofit retrofit = new Retrofit.Builder()
//設(shè)置OKHttpClient
.client(okHttp.INSTANCE.getOkHttpClient())
//Rx適配器
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
//gson轉(zhuǎn)化器
.addConverterFactory(GsonConverterFactory.create())
// 設(shè)置API基礎(chǔ)的Url
.baseUrl("https://www.baidu.com")
// 創(chuàng)建Retrofit
.build();
BaiduAPI api = retrofit.create(BaiduAPI.class);
Call<String> call = api.getInfo();
call.enqueue(new Callback<User>() {
@Override
public void onResponse(Call<User> call, Response<User> response) {
}
@Override
public void onFailure(Call<User> call, Throwable t) {
}
});
public interface BaiduAPI{
@GET("https://www.baidu.com)
Call<String> getInfo();
}
retrofit的使用還是比較簡單的致开,通過定義規(guī)定好的接口,通過Builder來創(chuàng)建Retrofit對象,通過create方法來創(chuàng)建接口的代理對象侣诺,然后調(diào)用定義好的方法就可以請求網(wǎng)絡(luò)來獲取數(shù)據(jù)了。
首先看一下Builder類吧
public static final class Builder {
// 平臺驗證 JAVA和Android氧秘,這里是Android
private final Platform platform;
// 真正發(fā)起OkHttp3請求的Call類
private @Nullable okhttp3.Call.Factory callFactory;
// 將字符url包裝為HttpUrl
private HttpUrl baseUrl;
// 轉(zhuǎn)換工廠集合年鸳,Retrofit可以插入多個轉(zhuǎn)換器,例如Gson丸相,jackson等
private final List<Converter.Factory> converterFactories = new ArrayList<>();
// 用于發(fā)起請求和接受相應的Call適配器集合
private final List<CallAdapter.Factory> adapterFactories = new ArrayList<>();
// 并發(fā)框架
private @Nullable Executor callbackExecutor;
// 是否立即加載所有的接口方法搔确,轉(zhuǎn)換為ServiceMethod對象,放在serviceMethodCache中
private boolean validateEagerly;
// 省略代碼
...
重點是create方法
public <T> T create(final Class<T> service) {
// 檢查接口的合法性,必須是接口妥箕,并且不能繼承其他接口
Utils.validateServiceInterface(service);
// 是否立即給接口中的所有方法生成包裝類滥酥,默認是false
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 {
// 如果方法對象的類時Object的對象,就走普通的代理方法
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
// 在Android平臺上是是返回false的
if (platform.isDefaultMethod(method)) {
return platform.invokeDefaultMethod(method, service, proxy, args);
}
// 生成ServiceMethod對象畦幢,這個對象是為了將請求的方法是配成HTTP Call
ServiceMethod<Object, Object> serviceMethod =
(ServiceMethod<Object, Object>) loadServiceMethod(method);
// 創(chuàng)建OkHttpCall對象
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
// 將OkHttpCall對象適配成我們需要的Call類型
return serviceMethod.callAdapter.adapt(okHttpCall);
}
});
}
可以看到這里用到了Java自帶的Proxy類坎吻,提供了動態(tài)代理支持,newProxyInstance方法會生成當前接口的代理類宇葱,并且放回改代理類的實例瘦真。正式動態(tài)代理設(shè)計模式,讓我們在觸發(fā)真正的網(wǎng)絡(luò)請求時添加其他的邏輯黍瞧。Retrofit正是利用這一點诸尽,利用注解,生成request和response印颤,將重復的工作封裝到這個類中您机,我們只需要專注于API接口的編寫即可,這也是AOP(面向切面編程)思想的一種體現(xiàn)年局。
每當我們調(diào)用代理對象的方法時际看,都會觸發(fā)InvocationHandler的invoke方法,它會把我們這次調(diào)用的方法method和參數(shù)args都回調(diào)過來矢否,拿到method后首先經(jīng)過loadServiceMethod
方法仲闽,將method作為key轉(zhuǎn)換為ServiceMethod對象,看一下這個類都做了什么僵朗?
/** Adapts an invocation of an interface method into an HTTP call. */
final class ServiceMethod<R, T> {
// Upper and lower characters, digits, underscores, and hyphens, starting with a character.
static final String PARAM = "[a-zA-Z][a-zA-Z0-9_-]*";
static final Pattern PARAM_URL_REGEX = Pattern.compile("\\{(" + PARAM + ")\\}");
static final Pattern PARAM_NAME_REGEX = Pattern.compile(PARAM);
final okhttp3.Call.Factory callFactory;
final CallAdapter<R, T> callAdapter;
private final HttpUrl baseUrl;
private final Converter<ResponseBody, R> responseConverter;
private final String httpMethod;
private final String relativeUrl;
private final Headers headers;
private final MediaType contentType;
private final boolean hasBody;
private final boolean isFormEncoded;
private final boolean isMultipart;
private final ParameterHandler<?>[] parameterHandlers;
ServiceMethod(Builder<R, T> builder) {
this.callFactory = builder.retrofit.callFactory();
this.callAdapter = builder.callAdapter;
this.baseUrl = builder.retrofit.baseUrl();
this.responseConverter = builder.responseConverter;
this.httpMethod = builder.httpMethod;
this.relativeUrl = builder.relativeUrl;
this.headers = builder.headers;
this.contentType = builder.contentType;
this.hasBody = builder.hasBody;
this.isFormEncoded = builder.isFormEncoded;
this.isMultipart = builder.isMultipart;
this.parameterHandlers = builder.parameterHandlers;
}
/** 將請求的方法轉(zhuǎn)換為一個請求需要Request對象 */
Request toRequest(@Nullable Object... args) throws IOException {
RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl, headers,
contentType, hasBody, isFormEncoded, isMultipart);
@SuppressWarnings("unchecked") // It is an error to invoke a method with the wrong arg types.
ParameterHandler<Object>[] handlers = (ParameterHandler<Object>[]) parameterHandlers;
int argumentCount = args != null ? args.length : 0;
if (argumentCount != handlers.length) {
throw new IllegalArgumentException("Argument count (" + argumentCount
+ ") doesn't match expected count (" + handlers.length + ")");
}
for (int p = 0; p < argumentCount; p++) {
handlers[p].apply(requestBuilder, args[p]);
}
return requestBuilder.build();
}
/** 經(jīng)過Convert處理赖欣,將原始的response body轉(zhuǎn)換成我們需要的類型 */
R toResponse(ResponseBody body) throws IOException {
return responseConverter.convert(body);
}
...
省略代碼
這個勒種最重要的兩個方法是toRequest
和toResponse
。我們請求時使用的request和最后得到的response就是由這兩個方法返回的验庙,還有其他大量的方法parseXXX
,都是講注解解析成具體的參數(shù)顶吮。
在得到ServiceMethod后,將它和請求參數(shù)一起作為參數(shù)創(chuàng)建了OkHttpCall對象壶谒,這個對象就是網(wǎng)絡(luò)請求所需要的對象云矫。OkHttpCall是一個裝飾類膳沽,真正的網(wǎng)絡(luò)請求時rawCall汗菜,而這個rawCall對象就是通過傳入的serviceMethod的toRequest
方法生成的。
看一下OkHttpCall類中幾個主要方法:
// OkHttpCall.java
//生成真正用于執(zhí)行請求任務的call
private okhttp3.Call createRawCall() throws IOException {
Request request = serviceMethod.toRequest(args);
okhttp3.Call call = serviceMethod.callFactory.newCall(request);
if (call == null) {
throw new NullPointerException("Call.Factory returned null.");
}
return call;
}
// 異步請求方法
public void enqueue(final Callback<T> callback) {
checkNotNull(callback, "callback == null");
okhttp3.Call call;
Throwable failure;
synchronized (this) {
if (executed) throw new IllegalStateException("Already executed.");
executed = true;
call = rawCall;
failure = creationFailure;
if (call == null && failure == null) {
try {
call = rawCall = createRawCall();
} catch (Throwable t) {
failure = creationFailure = t;
}
}
}
if (failure != null) {
callback.onFailure(this, failure);
return;
}
if (canceled) {
call.cancel();
}
// 異步請求
call.enqueue(new okhttp3.Callback() {
@Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse)
throws IOException {
Response<T> response;
try {
response = parseResponse(rawResponse);
} catch (Throwable e) {
callFailure(e);
return;
}
callSuccess(response);
}
@Override public void onFailure(okhttp3.Call call, IOException e) {
try {
callback.onFailure(OkHttpCall.this, e);
} catch (Throwable t) {
t.printStackTrace();
}
}
private void callFailure(Throwable e) {
try {
callback.onFailure(OkHttpCall.this, e);
} catch (Throwable t) {
t.printStackTrace();
}
}
private void callSuccess(Response<T> response) {
try {
callback.onResponse(OkHttpCall.this, response);
} catch (Throwable t) {
t.printStackTrace();
}
}
});
}
// 同步方法
public Response<T> execute() throws IOException {
okhttp3.Call call;
synchronized (this) {
if (executed) throw new IllegalStateException("Already executed.");
executed = true;
if (creationFailure != null) {
if (creationFailure instanceof IOException) {
throw (IOException) creationFailure;
} else {
throw (RuntimeException) creationFailure;
}
}
call = rawCall;
if (call == null) {
try {
call = rawCall = createRawCall();
} catch (IOException | RuntimeException e) {
creationFailure = e;
throw e;
}
}
}
if (canceled) {
call.cancel();
}
return parseResponse(call.execute());
}
// 本次請求結(jié)果經(jīng)過toResponse(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();
int code = rawResponse.code();
if (code < 200 || code >= 300) {
try {
// Buffer the entire body to avoid future I/O.
ResponseBody bufferedBody = Utils.buffer(rawBody);
return Response.error(bufferedBody, rawResponse);
} finally {
rawBody.close();
}
}
if (code == 204 || code == 205) {
rawBody.close();
return Response.success(null, rawResponse);
}
ExceptionCatchingRequestBody catchingBody = new ExceptionCatchingRequestBody(rawBody);
try {
T body = serviceMethod.toResponse(catchingBody);
return Response.success(body, rawResponse);
} catch (RuntimeException e) {
// If the underlying source threw an exception, propagate that rather than indicating it was
// a runtime exception.
catchingBody.throwIfCaught();
throw e;
}
}
上面代碼只截取了四個重要的方法
- createRawCall()生成用于請求的Call對象
- enqueue() 異步請求的方法
- excute() 同步執(zhí)行的方法
- parseResponse() 將結(jié)果通過Convert轉(zhuǎn)換為所需要的類型
Retrofit中的轉(zhuǎn)換器和適配器
Retrofit的定位是網(wǎng)絡(luò)框架(底層是由OkHttp負責)挑社,它的主要職責就是接口轉(zhuǎn)換為底層可以網(wǎng)絡(luò)請求的request陨界,和將網(wǎng)絡(luò)請求返回的response轉(zhuǎn)換為我們想要的數(shù)據(jù)類型。所以CallAdapter和Convert完成了這兩個功能痛阻。同時菌瘪,這兩個接口設(shè)計的十分靈活,易于拓展,可以和當今甚至未來的各種庫對接俏扩,也體現(xiàn)了Retrofit的優(yōu)勢糜工。
CallAdapter在Retrofit中有一個默認的實現(xiàn)類,在Platform類中的ExecutorCallAdapterFactory
录淡,RxJavaCallAdapterFactory捌木、SquareGuavaCallAdapterFactory等都做了很好的適配。
Convert是將一個類型轉(zhuǎn)換為另一個類型嫉戚,在retrofit2中的converter庫中已經(jīng)包含了我們通常使用的Gson刨裆,jackson等一些列的傳喚器,使用十分方便彬檀。
最后再總結(jié)一下帆啃,Retrofit使用到的設(shè)計模式
- 適配器模式
- 動態(tài)代理
- Builder模式
- 工廠模式
從okhttp和retrofit可以看出來,優(yōu)雅的設(shè)計框架窍帝,總是非常靈活的努潘,通過實現(xiàn)一些定義好的接口,然后根據(jù)業(yè)務的場景需求將相應的邏輯無縫插入到這些框架中坤学,即實現(xiàn)了功能又不會破壞代碼整體的結(jié)構(gòu)慈俯,這才是“優(yōu)雅”的框架。我們在設(shè)計框架時拥峦,也應該充分借鑒這一點贴膘,為用戶預留充足的自定義空間,這樣才能造出受歡迎的輪子略号。