携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第1天,点击查看活动详情
Retrofit源码系列文章:
- 挖一挖Retrofit源码(一)
- 挖一挖Retrofit源码(二)
- Retrofit之CallAdapter解析
- Retrofit之自定义CallAdapter
- Retrofit之Converter解析
简介
Retrofit是一个基于OkHttp的网络请求框架,对OkHttp的请求和结果进行处理,Retrofit仅负责网络请求接口的封装并自动生成实际网络请求的代码,而网络请求的工作本质上是OkHttp完成的。
应用发起网络请求后,实际上是使用 Retrofit 接口层封装请求参数、Header、Url等信息,之后由OkHttp完成后续的请求操作,OkHttp将服务器返回的原始数据交给Retrofit,Retrofit根据用户的需求对结果进行解析。
PS:本文基于Retrofit版本2.8.0
源码解析
1. 创建Retrofit实例
val retrofit = Retrofit.Builder()
.baseUrl("https://test.com")
.addConverterFactory(GsonConverterFactory.create())
.build()
复制代码
Retrofit实例是使用建造者模式通过Builder类创建的,并初始化一些配置项,下面将对创建Retrofit实例进行详细分析。
1.1 Retrofit类
public final class Retrofit {
private final Map<Method, ServiceMethod<?>> serviceMethodCache = new ConcurrentHashMap<>();
final okhttp3.Call.Factory callFactory; //生产网络请求器(Call)的工厂,默认使用OkHttp
final HttpUrl baseUrl; //url地址
final List<Converter.Factory> converterFactories; //数据转换器(converter)的工厂List
final List<CallAdapter.Factory> callAdapterFactories; //生产网络请求适配器(CallAdapter)的工厂List
final @Nullable Executor callbackExecutor; //回调方法执行器
final boolean validateEagerly; //是否提前对业务接口中的注解进行验证转换的标志位
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;
}
//省略其他代码
......
}
复制代码
其中callFactory、converterFactories、callAdapterFactories体现了工厂模式,使用者不需要关心具体参数就可以实例化出所需要的类。
1.2 Builder类
public static final class Builder {
private final Platform platform;
private @Nullable okhttp3.Call.Factory callFactory;
private @Nullable HttpUrl baseUrl;
private final List<Converter.Factory> converterFactories = new ArrayList<>();
private final List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>();
private @Nullable Executor callbackExecutor;
private boolean validateEagerly;
//①Builder的有参构造函数
Builder(Platform platform) {
this.platform = platform;
}
//②Builder的无参构造函数
public Builder() {
this(Platform.get());
}
......
}
复制代码
可以看出Builder类的成员变量和Retrofit类的是对应的,所以Retrofit类的成员变量基本上是通过Builder类来配置。
代码里①接收了Platform对象并设进Builder类的Platform,而②则是用this调用了自己的有参构造函数①并通过调用Platform.get()传入Platform对象,即Builder的构造函数中检测了当前的运行平台。
1.3 addConverterFactory方法
public Builder addConverterFactory(Converter.Factory factory) {
converterFactories.add(Objects.requireNonNull(factory, "factory == null"));
return this;
}
复制代码
前面创建Retrofit实例的时候是把GsonConverterFactory.create()传进了addConverterFactory方法,而GsonConverterFactory.create()实际上是创建并返回了一个含有Gson对象实例的GsonConverterFactory,即指定Retrofit使用Gson进行解析数据,也可以使用其他解析方式(如Json、XML或Protocobuf)或者可以使用自定义数据解析器(必须继承Converter.Factory)。addCallAdapterFactory方法也是类似的,可以添加Retrofit内置的CallAdapterFactory,也可以使用自定义的适配器。
1.4 build方法
在创建Retrofit.Builder对象并进行自定义配置后,我们就要调用build方法来构造出Retrofit对象了,下面来看看build()里干了什么:
public Retrofit build() {
//必须要配置baseUrl
if (baseUrl == null) {
throw new IllegalStateException("Base URL required.");
}
//配置网络请求执行器,若无指定则默认使用OkHttp
okhttp3.Call.Factory callFactory = this.callFactory;
if (callFactory == null) {
callFactory = new OkHttpClient();
}
//配置回调方法执行器,Android平台下默认为MainThreadExecutor
//MainThreadExecutor的作用:切换线程(子->>主线程),并在主线程(UI线程)中执行回调方法
Executor callbackExecutor = this.callbackExecutor;
if (callbackExecutor == null) {
callbackExecutor = platform.defaultCallbackExecutor();
}
// Make a defensive copy of the adapters and add the default Call adapter.
//配置网络请求适配器工厂,将默认适配器工厂DefaultCallAdapterFactory添加至List末尾
List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor));
// Make a defensive copy of the converters.
//配置数据转换器工厂
List<Converter.Factory> converterFactories = new ArrayList<>(
1 + this.converterFactories.size() + platform.defaultConverterFactoriesSize());
// Add the built-in converter factory first. This prevents overriding its behavior but also
// ensures correct behavior when using converters that consume all types.
//首先添加默认的BuiltInConverters,最后添加检测平台环境时默认返回的数据转换器OptionalConverterFactory
converterFactories.add(new BuiltInConverters());
converterFactories.addAll(this.converterFactories);
converterFactories.addAll(platform.defaultConverterFactories());
return new Retrofit(callFactory, baseUrl, unmodifiableList(converterFactories),
unmodifiableList(callAdapterFactories), callbackExecutor, validateEagerly);
}
复制代码
可以看到在build()中做了:检查配置、设置默认配置、创建Retrofit对象,而且在执行build()之前只有baseUrl()是必须调用来设置访问地址的,其余方法则是可选的,没有指定就使用默认配置。另外:
- callFactory负责创建HTTP请求,HTTP请求被抽象为了okhttp3.Call类,它表示一个已经准备好,可以随时执行的HTTP请求
- CallAdapter负责把服务器的原始返回数据封装成Retrofit的Call对象(注意和okhttp3.Call区分开来),通过Call发起HTTP请求,并把从服务器拿到的数据(通过okhttp3.Call实现)转换为接口方法声明的返回类型T(通过Converter<F, T> 实现)
1.5 总结
Retrofit创建实例时主要涉及了以下的配置项:
- 网络请求地址baseUrl
- 网络请求执行器工厂callFactory:默认使用OkHttpClient,通过OkHttp处理后续网络请求和响应
- 回调方法执行器callbackExecutor:默认使用MainThreadExecutor
- 网络请求适配器工厂集合callAdapterFactories:默认添加DefaultCallAdapterFactory
- 数据转换器工厂集合converterFactories:默认添加BuiltInConverters和OptionalConverterFactory,BuiltInConverters直接取对应的值并未转换,可以通过设置GsonConverterFactory来用Gson解析json数据
由于使用了建造者模式,所以创建Retrofit实例时并不需要关心配置细节。
2. 创建网络请求接口实例
//定义网络请求的接口类
interface TestApi {
@GET("/.../...")
fun getDataA(): Call<DataBean> //①不使用协程
@GET("/.../...")
suspend fun getDataB(): DataBean //②使用协程
}
//创建网络请求接口实例
val testApi = retrofit.create(TestApi::class.java)
//调用接口方法①
fun getDataA() {
testApi.getDataA().enqueue(object : Callback<DataBean> {
override fun onResponse(
call: Call<DataBean>,
response: Response<DataBean>
) {
//成功逻辑
}
override fun onFailure(call: Call<DataBean>, t: Throwable) {
//失败逻辑
}
})
}
//调用接口方法②
suspend fun getDataB() {
try {
testApi.getDataB()
//成功逻辑
} catch (e: Exception) {
//失败逻辑
}
}
复制代码
可以看到请求接口里的请求方法类型及请求url的后半部分是通过注解来标注的,而DataBean是该方法的返回类型(一个自定义的Data类)
另外,在没有给Retrofit添加任何CallAdapterFactory的情况下:
- 接口方法①的返回类型就必须是Call<?>,不能为其他类型;
- 接口方法②由于用了suspend关键字,挂起了之后就可以无需使用Call类型返回结果,而是直接返回数据类对象。
接口创建完之后就是调用Retrofit的create方法来生成接口的动态代理对象,下面就来看看具体是怎么实现动态代理的。
2.1 create方法
public <T> T create(final Class<T> 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);
}
//获取方法的对应ServiceMethod对象并调用invoke方法
return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
}
});
}
复制代码
①处的方法除了校验接口之外,还对是否提前解析接口中的所有方法进行了判断:
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);
......//验证接口泛型参数及接口类型
//如果设置了预解析则提前解析接口中的所有方法
if (validateEagerly) {
Platform platform = Platform.get();
for (Method method : service.getDeclaredMethods()) {
if (!platform.isDefaultMethod(method) && !Modifier.isStatic(method.getModifiers())) {
loadServiceMethod(method);
}
}
}
}
复制代码
如果初始化Retrofit实例的时候设置了预解析,就会提前给接口中的每个方法解析生成对应的一个ServiceMethod对象并存入缓存,否则就调用方法的时候再动态解析,具体是怎么解析的看loadServiceMethod这个方法(后面会说明)
在create方法中主要的代码就是②处的Proxy.newProxyInstance,当把请求接口类传进create里的时候就会动态生成并返回一个代理对象,而接口方法被调用的时候实际上是由动态代理对象将方法转发到InvocationHandler的invoke方法中处理。因为实际调用的接口方法不是默认方法,那真正调用到的就是loadServiceMethod(method).invoke(args != null ? args : emptyArgs),这也是后续分析的关键入口。
2.2 loadServiceMethod方法
ServiceMethod<?> loadServiceMethod(Method method) {
//从缓存中获取
ServiceMethod<?> result = serviceMethodCache.get(method);
if (result != null) return result;
//加锁,创建该方法的ServiceMethod对象并存入缓存
synchronized (serviceMethodCache) {
result = serviceMethodCache.get(method);
if (result == null) {
result = ServiceMethod.parseAnnotations(this, method);
serviceMethodCache.put(method, result);
}
}
return result;
}
复制代码
接口方法传进loadServiceMethod后优先是从缓存中查对应的ServiceMethod对象,获取到了就直接返回,否则加锁后创建这个方法的ServiceMethod对象,以method为主键存入一个叫serviceMethodCache的map中。
可以看出来同一个接口的方法只会解析一次,每一个调用过的方法都会存在对应的ServiceMethod对象,因为建接口实例的时候传进的是class对象(TestApi::class.java),class对象在进程内单例的,所以获取到的同一个method也是单例的,因此这里的缓存有效。
这段代码主要是为了拿到一个ServiceMethod对象,而生成ServiceMethod对象是调用了ServiceMethod的parseAnnotations方法,ServiceMethod里面到底是什么东东,parseAnnotations方法具体干了什么,下面继续来扒一扒。
2.3 ServiceMethod类
abstract class ServiceMethod<T> {
static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
//根据方法的注解生成Request的工厂类实例
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.");
}
//生成并返回HttpServiceMethod对象
return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
}
abstract @Nullable T invoke(Object[] args);
}
复制代码
回看2.1中获取的ServiceMethod对象实际上是这里的HttpServiceMethod对象,调用链路为loadServiceMethod -> ServiceMethod.parseAnnotations -> HttpServiceMethod.parseAnnotations,继续往下看HttpServiceMethod的parseAnnotations方法。
2.4 HttpServiceMethod类parseAnnotations方法
static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
Retrofit retrofit, Method method, RequestFactory requestFactory) {
boolean isKotlinSuspendFunction = requestFactory.isKotlinSuspendFunction;
boolean continuationWantsResponse = false;
boolean continuationBodyNullable = false;
Annotation[] annotations = method.getAnnotations();
Type adapterType;
if (isKotlinSuspendFunction) {
//Kotlin协程检测
......
adapterType = new Utils.ParameterizedTypeImpl(null, Call.class, responseType);
annotations = SkipCallbackExecutorImpl.ensurePresent(annotations);
} else {
//获取方法返回类型
adapterType = method.getGenericReturnType();
}
CallAdapter<ResponseT, ReturnT> callAdapter =
createCallAdapter(retrofit, method, adapterType, annotations);
Type responseType = callAdapter.responseType();
......//校验responseType
Converter<ResponseBody, ResponseT> responseConverter =
createResponseConverter(retrofit, method, responseType);
okhttp3.Call.Factory callFactory = retrofit.callFactory;
if (!isKotlinSuspendFunction) {
return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);
} else if (continuationWantsResponse) {
//noinspection unchecked Kotlin compiler guarantees ReturnT to be Object.
return (HttpServiceMethod<ResponseT, ReturnT>) new SuspendForResponse<>(requestFactory,
callFactory, responseConverter, (CallAdapter<ResponseT, Call<ResponseT>>) callAdapter);
} else {
//noinspection unchecked Kotlin compiler guarantees ReturnT to be Object.
return (HttpServiceMethod<ResponseT, ReturnT>) new SuspendForBody<>(requestFactory,
callFactory, responseConverter, (CallAdapter<ResponseT, Call<ResponseT>>) callAdapter,
continuationBodyNullable);
}
}
复制代码
parseAnnotations方法最后返回的是HttpServiceMethod对象,如果是非挂起方法则直接返回CallAdapted,挂起方法并且返回类型为Response则返回SuspendForResponse,否则返回SuspendForBody。
现在了解了Retrofit生成动态代理对象的逻辑,那动态代理是怎么进行网络请求的和回调又是如何处理的呢?下一篇将继续分析。