Retrofit
可以说和 OkHttp
是亲兄弟了,它们都是由 Square 公司推出的网络请求库,并且 Retrofit
实际上是基于 OkHttp
实现的,它在 OkHttp
现有功能的基础上进行了封装,支持通过注解进行网络请求参数的配置,同时对数据返回后的解析、序列化进行了统一的包装,甚至在近期引入了对协程对支持。
今天就让我们一起来看看 Retrofit
是如何在 OkHttp
这样一个已经固定的框架的基础上,优雅的进行封装并拓展功能的。
本篇源码解析基于 Retrofit v2.7.0
个人博客:https://blog.N0tExpectErr0r.cn
小专栏:https://xiaozhuanlan.com/N0tExpectErr0r
基本使用
我们首先来看看 Retrofit
的基本使用,来对它有个大致的了解。
首先,我们可以构建一个如下的请求 Service 类,它里面对各个请求的方法及参数通过注解进行了标注:
public interface GitHubService {
@GET("users/{user}/repos")
Call<List<Repo>> listRepos(@Path("user") String user);
}
之后,我们可以构建一个 Retrofit
对象,并通过 Retrofit.create
方法传入对应 class
从而构建对应的 Service 对象:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.build();
GitHubService service = retrofit.create(GitHubService.class);
之后,我们调用 service
中对应的方法,就可以获取到 Call
对象了。
通过对 Call
对象调用 enqueue
就可以实现对请求的异步调用,而通过 execute
方法则可以实现请求的同步调用。
Retrofit 对象的构建
Retrofit 采用了 Builder 模式进行了构建,在 Builder 中可以进行非常多的配置,其中可以对 baseUrl
、okhttpClient
、converterFactory
、callAdapterFactory
等进行设置。
这里没什么特别的,都是一些简单的赋值,就不再关注了,我们只需要看看最后 Retrofit
被传入了哪些参数。它最后调用了下面这个构造函数对参数进行了初始化。
Retrofit(okhttp3.Call.Factory callFactory, HttpUrl baseUrl,
List<Converter.Factory> converterFactories, List<CallAdapter.Factory> callAdapterFactories,
@Nullable Executor callbackExecutor, boolean validateEagerly) {
this.callFactory = callFactory;
this.baseUrl = baseUrl;
this.converterFactories = converterFactories; // Copy+unmodifiable at call site.
this.callAdapterFactories = callAdapterFactories; // Copy+unmodifiable at call site.
this.callbackExecutor = callbackExecutor;
this.validateEagerly = validateEagerly;
}
Service 对象的创建
动态代理创建 Service 代理类
接着我们看到自己定义的 interface
是如何仅仅靠传递 class
给 Retrofit.create
就能实现实例的获取的,它明明只是个接口呀?
public <T> T create(final Class<T> service) {
// 对 Service 的接口进行检测
validateServiceInterface(service);
// 通过动态代理构建代理对象
return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] {
service },
new InvocationHandler() {
private final Platform platform = Platform.get();
private final Object[] emptyArgs = new Object[0];
@Override public @Nullable Object invoke(Object proxy, Method method,
@Nullable Object[] args) throws Throwable {
// Object 类的方法照常调用
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
// 如果是对应平台本身的类就有的方法,照常调用
if (platform.isDefaultMethod(method)) {
return platform.invokeDefaultMethod(method, service, proxy, args);
}
// 否则通过 loadServiceMethod 方法获取到对应 Method 并 invoke
return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
}
});
}
可以看到,实际上 Service
对象的获取是通过动态代理实现的。这里首先通过 validateServiceInterface
方法对接口进行了检测,之后通过动态代理对该接口进行了代理。
对于 Object
类本身独有以及对应平台本身就存在的方法,就照常调用,否则通过 loadServiceMethod
对 Service
中对应的 Method
对象进行处理,之后对其调用 invoke
方法。
这里说明了 Retrofit
不是在创建 Service 接口对应对象时就立即对所有该接口中的所有方法都进行注解的解析,而是采用了在方法被调用时才进行注解的解析这种懒加载的思想。
接着我们看看 validateServiceInterface
方法:
private void validateServiceInterface(Class<?> service) {
// 判断是否是接口
if (!service.isInterface()) {
throw new IllegalArgumentException("API declarations must be interfaces.");
}
// 判断该接口及其继承的所有接口是否包含了范型参数,如果包含则抛出异常
Deque<Class<?>> check = new ArrayDeque<>(1);
check.add(service);
while (!check.isEmpty()) {
Class<?> candidate = check.removeFirst();
if (candidate.getTypeParameters().length != 0) {
StringBuilder message = new StringBuilder("Type parameters are unsupported on ")
.append(candidate.getName());
if (candidate != service) {
message.append(" which is an interface of ")
.append(service.getName());
}
throw new IllegalArgumentException(message.toString());
}
Collections.addAll(check, candidate.getInterfaces());
}
// 如果在创建Retrofit时设置了很急切地对Service的方法进行处理,则对非平台独有且非static的方法通过 loadServiceMethod 方法进行处理。
if (validateEagerly) {
Platform platform = Platform.get();
for (Method method : service.getDeclaredMethods()) {
if (!platform.isDefaultMethod(method) && !Modifier.isStatic(method.getModifiers())) {
loadServiceMethod(method);
}
}
}
}
首先,这个方法对 service
进行了检测,保证了它是一个接口并且它和它继承的类中没有范型参数。
之后如果在 Retrofit
创建时设置 validateEagerly
为 true 的话,会对非平台独有且非static的方法通过 loadServiceMethod
方法提前进行处理。
Service 中方法的解析
那么我们来看看 loadServiceMethod
究竟做了些什么:
ServiceMethod<?> loadServiceMethod(Method method) {
ServiceMethod<?> result = serviceMethodCache.get(method);
if (result != null) return result;
synchronized (serviceMethodCache) {
result = serviceMethodCache.get(method);
if (result == null) {
result = ServiceMethod.parseAnnotations(this, method);
serviceMethodCache.put(method, result);
}
}
return result;
}
首先它会采用 Double Check 的方式尝试从 serviceMethodCache
缓存中获取 ServiceMethod
对象,如果获取不到则通过 ServiceMethod.parseAnnotations
方法对该 Method
的注解进行处理并将得到的 ServiceMethod
对象加入了缓存。
也就是说为了避免多次对方法的注解进行处理,Retrofit
采用了一个 serviceMethodCache
对解析后的 ServiceMethod
进行缓存。
接着我们就来看看,parseAnnotations
方法是如何对方法的注解进行解析的。
static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);
Type returnType = method.getGenericReturnType();
if (Utils.hasUnresolvableType(returnType)) {
throw methodError(method,
"Method return type must not include a type variable or wildcard: %s", returnType);
}
if (returnType == void.class) {
throw methodError(method, "Service methods cannot return void.");
}
return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
}
这里先通过 RequestFactory.parseAnnotations
方法对注解解析并获得了一个 RequestFactory
对象。
之后又通过 HttpServiceMethod.parseAnnotations
方法传入了 requestFactory
继续进行注解的解析并获得 ServiceMethod
对象。
注解解析
我们先看看 RequestFactory.parseAnnotations
:
static RequestFactory parseAnnotations(Retrofit retrofit, Method method) {
return new Builder(retrofit, method).build();
}
它把 Method
传入 Builder 从而构建了一个新的 RequestFactory
:
Builder(Retrofit retrofit, Method method) {
this.retrofit = retrofit;
this.method = method;
this.methodAnnotations = method.getAnnotations();
this.parameterTypes = method.getGenericParameterTypes();
this.parameterAnnotationsArray = method.getParameterAnnotations();
}
Builder 中通过反射获取到了 method
所包含的注解、参数包含的范型以及参数的注解。
接着看看 build
方法:
RequestFactory build() {
for (Annotation annotation : methodAnnotations) {
// 遍历方法注解对每个注解进行解析
parseMethodAnnotation(annotation);
}
// ...异常的处理
// 对参数进行解析
int parameterCount = parameterAnnotationsArray.length;
parameterHandlers = new ParameterHandler<?>[parameterCount];
for (int p = 0, lastParameter = parameterCount - 1; p < parameterCount; p++) {
parameterHandlers[p] =
parseParameter(p, parameterTypes[p], parameterAnnotationsArray[p], p == lastParameter);
}
// ... 异常的处理
return new RequestFactory(this);
}
在 build
方法中主要是对方法的每个注解调用了 parseMethodAnnotation
进行了解析,并且对每个参数调用了 parseParamter
方法解析为了 ParamterHandler
对象。
parseMethodAnnotation
的代码如下:
private void parseMethodAnnotation(Annotation annotation) {
if (annotation instanceof DELETE