本次分析的要点:
- Retrofit中的动态代理和整个流程(Proxy、ServiceMethod、OkHttpCall)
- Retrofit中的转换器和适配器(Converter、CallAdapter)
Retrofit整体流程和动态代理
首先我们回顾一下retrofit的使用demo
Retrofit retrofit = new Retrofit.Builder()
//设置OKHttpClient
.client(okHttp.INSTANCE.getOkHttpClient())
//Rx适配器
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
//gson转化器
.addConverterFactory(GsonConverterFactory.create())
// 设置API基础的Url
.baseUrl("https://www.baidu.com")
// 创建Retrofit
.build();
BaiduAPI api = retrofit.create(BaiduAPI.class);
Call call = api.getInfo();
call.enqueue(new Callback() {
@Override
public void onResponse(Call call, Response response) {
}
@Override
public void onFailure(Call call, Throwable t) {
}
});
public interface BaiduAPI{
@GET("https://www.baidu.com)
Call getInfo();
}
retrofit的使用还是比较简单的,通过定义规定好的接口,通过Builder来创建Retrofit对象,通过create方法来创建接口的代理对象,然后调用定义好的方法就可以请求网络来获取数据了。
首先看一下Builder类吧
public static final class Builder {
// 平台验证 JAVA和Android,这里是Android
private final Platform platform;
// 真正发起OkHttp3请求的Call类
private @Nullable okhttp3.Call.Factory callFactory;
// 将字符url包装为HttpUrl
private HttpUrl baseUrl;
// 转换工厂集合,Retrofit可以插入多个转换器,例如Gson,jackson等
private final List converterFactories = new ArrayList<>();
// 用于发起请求和接受相应的Call适配器集合
private final List adapterFactories = new ArrayList<>();
// 并发框架
private @Nullable Executor callbackExecutor;
// 是否立即加载所有的接口方法,转换为ServiceMethod对象,放在serviceMethodCache中
private boolean validateEagerly;
// 省略代码
...
重点是create方法
public T create(final Class 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
可以看到这里用到了Java自带的Proxy类,提供了动态代理支持,newProxyInstance方法会生成当前接口的代理类,并且放回改代理类的实例。正式动态代理设计模式,让我们在触发真正的网络请求时添加其他的逻辑。Retrofit正是利用这一点,利用注解,生成request和response,将重复的工作封装到这个类中,我们只需要专注于API接口的编写即可,这也是AOP(面向切面编程)思想的一种体现。
每当我们调用代理对象的方法时,都会触发InvocationHandler的invoke方法,它会把我们这次调用的方法method和参数args都回调过来,拿到method后首先经过loadServiceMethod
方法,将method作为key转换为ServiceMethod对象,看一下这个类都做了什么?
/** Adapts an invocation of an interface method into an HTTP call. */
final class ServiceMethod {
// 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 callAdapter;
private final HttpUrl baseUrl;
private final Converter 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 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;
}
/** 将请求的方法转换为一个请求需要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[] handlers = (ParameterHandler[]) 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();
}
/** 经过Convert处理,将原始的response body转换成我们需要的类型 */
R toResponse(ResponseBody body) throws IOException {
return responseConverter.convert(body);
}
...
省略代码
这个勒种最重要的两个方法是toRequest
和toResponse
。我们请求时使用的request和最后得到的response就是由这两个方法返回的,还有其他大量的方法parseXXX
,都是讲注解解析成具体的参数。
在得到ServiceMethod后,将它和请求参数一起作为参数创建了OkHttpCall对象,这个对象就是网络请求所需要的对象。OkHttpCall是一个装饰类,真正的网络请求时rawCall,而这个rawCall对象就是通过传入的serviceMethod的toRequest
方法生成的。
看一下OkHttpCall类中几个主要方法:
// OkHttpCall.java
//生成真正用于执行请求任务的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 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 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 response) {
try {
callback.onResponse(OkHttpCall.this, response);
} catch (Throwable t) {
t.printStackTrace();
}
}
});
}
// 同步方法
public Response 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());
}
// 本次请求结果经过toResponse(Converter)处理后返回
Response 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() 同步执行的方法
- parseResponse() 将结果通过Convert转换为所需要的类型
Retrofit中的转换器和适配器
Retrofit的定位是网络框架(底层是由OkHttp负责),它的主要职责就是接口转换为底层可以网络请求的request,和将网络请求返回的response转换为我们想要的数据类型。所以CallAdapter和Convert完成了这两个功能。同时,这两个接口设计的十分灵活,易于拓展,可以和当今甚至未来的各种库对接,也体现了Retrofit的优势。
CallAdapter在Retrofit中有一个默认的实现类,在Platform类中的ExecutorCallAdapterFactory
,RxJavaCallAdapterFactory、SquareGuavaCallAdapterFactory等都做了很好的适配。
Convert是将一个类型转换为另一个类型,在retrofit2中的converter库中已经包含了我们通常使用的Gson,jackson等一些列的传唤器,使用十分方便。
最后再总结一下,Retrofit使用到的设计模式
- 适配器模式
- 动态代理
- Builder模式
- 工厂模式
从okhttp和retrofit可以看出来,优雅的设计框架,总是非常灵活的,通过实现一些定义好的接口,然后根据业务的场景需求将相应的逻辑无缝插入到这些框架中,即实现了功能又不会破坏代码整体的结构,这才是“优雅”的框架。我们在设计框架时,也应该充分借鉴这一点,为用户预留充足的自定义空间,这样才能造出受欢迎的轮子。