本文出自门心叼龙的博客,属于原创类容,未经允许,不得转载。
本专栏的同步视频教程已经发布到CSDN学院:https://edu.csdn.net/course/detail/30408
上一篇文章我们通过12个小案例,给大家演示了retrofit的各种用法,retrofit如果从用的角度上来讲,他玩儿的就是个注解,通过接口方法表示一个请求,通过方法注解,参数注解来封装请求的参数。同样是对OkHttp的封装,我们在okhttp专题也同样对其封装过,一种是集中式封装,一种是链接封装,但是retrofit用着更加简单,通过简单的注解配置就能表示一个网络请求,另外请求适配器和响应转换器的自定义使他的扩展性更强,在加上RxJava的配合使用,就更加强大了,还有与kotlin协程的新玩法,简直就是帅呆了,这个后面再讲,今天这篇文章主要是给大家分享retrofit源码解析。我们不但要会用,还要知道它底层的工作原理。读书破万卷下笔如有神,阅读源码也是同样的道理:读码千万行,下键如有神。
文章目录
特别提示,本专题是基于最新版:retrofit 2.9.0研究的,我们还是以用户登录的接口为例:
public void loginGet(View view) {
Log.v("MYTAG","loginGet start...");
//1.利用建造者模式创建一个Retrofit
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://192.168.0.189:8080")
.addConverterFactory(GsonConverterFactory.create())
.build();
//2.通过动态代理创建一个用户服务
UserService userService = retrofit.create(UserService.class);
//3.创建一个登录命令
Call<BaseResponse> loginCall = userService.loginGet("mxdl", "123456");
//4.执行登录命令
loginCall.enqueue(new Callback<BaseResponse>() {
@Override
public void onResponse(Call<BaseResponse> call, Response<BaseResponse> response) {
Log.v("MYTAG", "onResponse start...");
Log.v("MYTAG", response.body().toString());
}
@Override
public void onFailure(Call<BaseResponse> call, Throwable t) {
Log.v("MYTAG", "onFail start...");
Log.v("MYTAG", t.toString());
}
});
}
整个过程分为四步,首先通过建造者模式创建了一个Retrofit,无处不在的建造者模式,JakeWhorton大神已经把建造者模式用的登峰造极了,接着调动retrofit的create方法创建了一个用户服务UserService,有了服务就可以调用它的具体的方法来创建一个请求命令,有了命令就可以执行请求了,这就是一个网络请求的使用流程。
1.Retrofit的创建
我们先看第一步retrofit的创建,我们可以第一步再次进行拆分成四个小的步骤,即如下图所示:
接下来我们看看这四小步的内部实现,它背后都做了哪些操作。
1.Builder的创建
我们先看第一步,实例化了一个Retrofit的建造者Builder,Buidler是Retrofit的静态内部类,建造者模式在OkHttp的使用也是无处不在,它的主要作用就是将一个复杂对象的创建与表示相分离,使用相同的构建过程创建出不同的对象表示。
在看Builder声明在Retrofit的内部,这是建造者模式一般通用的一个套路,下图是Retrofit类的注释,这是官方权威定义。
Retrofit它能适配一个Java接口为Http调用命令,通过在定义的方法上添加注解来定义怎样去发出一个Request请求。通过建造者创建一个实例,把你的接口传递给他的create方法去生成一个它的实现。注释的最下方是该类的作者信息,Bob Lee和JakeWharton,原来这是JakeWharton大神的真迹,是否有一种要收藏该代码的冲动。这类代码量也就600多行,下面这是Retrofit类的一个大纲视图:
Retrofit相当就是一个统帅,管理者整个家庭里面的大小事务,他有一个内部类Builder,一个核心方法create和五个非常重要的成员变量,这五个变量相对于就是三国里的五虎上将,他们各自都身怀绝技,完成不同的使命。
public final class Retrofit {
//请求方法缓存容器
private final Map<Method, ServiceMethod<?>> serviceMethodCache = new ConcurrentHashMap<>();
//请求执行器
final okhttp3.Call.Factory callFactory;
//请求地址
final HttpUrl baseUrl;
//请求响应转换器
final List<Converter.Factory> converterFactories;
//请求适配器
final List<CallAdapter.Factory> callAdapterFactories;
//线程切换器
final @Nullable Executor callbackExecutor;
//是否立即校验
final boolean validateEagerly;
五虎上将:
-
serviceMethodCache:请求存储器,缓存我们的请求方法
-
callFactory:请求执行器,也就是我们okhttp,retrofit是基于okhttp的,执行网络请求
-
converterFactories:数据转换器,包括请求参数的转换和响应结果的转换
-
callAdapterFactories:请求适配器,默认是Call,通过他可以适配成我们想要的结果,例如适配为RxJava的Observeable等
-
callbackExecutor:线程切换器,将子线程切换到UI主线程,以便网络请求之后更新UI
这五虎上将在Retrofit大元帅的统一领导下,他们各司其职,协同工作,他们有一个共同的小目标:完成一个网络请求,对Retrofit类有了大致的了解之后,我在回过头看Builder类的构造方法:
public static final class Builder {
...
//注释1
Builder(Platform platform) {
this.platform = platform;
}
public Builder() {
this(Platform.get());
}
...
}
Builder无参的构造器会调用注释1处的有参构造器,我们注意了构造器默认就初始化了一个参数Platform,这到底是什么鬼?从字面意思上讲是平台的意思,我看看它的Platform的get方法:
class Platform {
private static final Platform PLATFORM = findPlatform();
//注释1
static Platform get() {
return PLATFORM;
}
//注释1:
private static Platform findPlatform() {
return "Dalvik".equals(System.getProperty("java.vm.name"))
? new Android() //
: new Platform(true);
}
注释1处的get方法返回就是就是Platform类的静态成员变量:PLATFORM,而它是通过findPlatform方法初始化的,我们直接看注释2处的findPlatform方法,这是一个三目运算表达式,如果是android虚拟机Dalvik则返回Android对象,否则返回Platform对象,也就是说Retrofit它支持两大平台,不只是Android平台中能用,Java平台也可以使用,下面是Platform的大纲视图:
Android是Platform的子类,从下面的那些default开头的方法我们也能看出来,Platform类是给Retrofit大元帅的五虎上将提供默认实现的,如:线程切换器,请求适配器,数据转换器,需要我们注意的是它的defaultCallAdapterFactories方法和defaultCallbackExecutor方法:
class Platform {
...
List<? extends CallAdapter.Factory> defaultCallAdapterFactories(
@Nullable Executor callbackExecutor) {
DefaultCallAdapterFactory executorFactory = new DefaultCallAdapterFactory(callbackExecutor);
return hasJava8Types
? asList(CompletableFutureCallAdapterFactory.INSTANCE, executorFactory)
: singletonList(executorFactory);
}
@Nullable
Executor defaultCallbackExecutor() {
return null;
}
...
}
defaultCallAdapterFactories提供了默认的数据转换器,如果没有特别指定的话,默认就会使用Platform给我们提供的默认转换器DefaultCallAdapterFactory,本例的登录接口最终接口方法适配的时候就是用到它,defaultCallbackExecutor方法Platform默认实现是空实现返回了个null,它的子类Android对这个方法进行了复写,实现如下:
static final class Android extends Platform {
...
@Override
public Executor defaultCallbackExecutor() {
return new MainThreadExecutor();
}
...
static final class MainThreadExecutor implements Executor {
private final Handler handler = new Handler(Looper.getMainLooper());
@Override
public void execute(Runnable r) {
handler.post(r);
}
}
@IgnoreJRERequirement // Only called on API 26+.
@Nullable
Object invokeDefaultMethod(Method method, Class<?> declaringClass, Object object, Object... args)
throws Throwable {
Lookup lookup =
lookupConstructor != null
? lookupConstructor.newInstance(declaringClass, -1 /* trusted */)
: MethodHandles.lookup();
return lookup.unreflectSpecial(method, declaringClass).bindTo(object).invokeWithArguments(args);
}
}
返回的就是线程切换器MainThreadExecutor,它继承了Executor,内部封装了Handler,这样通过他就可以轻松的实现子线程到主线程的切换。直到到现在第一步的Retrofit的建造者Builder就创建好了,其实就是实例化了一个默认的平台Platform,Platform提供了默认的线程切换器,请求适配器和数据转化器。另外Platform还有一个额外的工作就是通过反射执行我们接口服务里面定义的default方法,因为从Java1.8开始接口里面可以提供默认的方法实现,如果是一个标准的网络接口方法就需要注解解析等后续操作。
2.设置url
建造者初始化完毕,接着就需要设置网络请求的地址url,注意了url的设置这是必须的,否则在构建的最后一步builer()的时候会直接强制抛异常,从而终止程序的继续向下执行。
public static final class Builder {
...
public Builder baseUrl(URL baseUrl) {
Objects.requireNonNull(baseUrl, "baseUrl == null");
return baseUrl(HttpUrl.get(baseUrl.toString()));
}
public Builder baseUrl(String baseUrl) {
Objects.requireNonNull(baseUrl, "baseUrl == null");
return baseUrl(HttpUrl.get(baseUrl));
}
public Builder baseUrl(HttpUrl baseUrl) {
Objects.requireNonNull(baseUrl, "baseUrl == null");
List<String> pathSegments = baseUrl.pathSegments();
if (!"".equals(pathSegments.get(pathSegments.size() - 1))) {
throw new IllegalArgumentException("baseUrl must end in /: " + baseUrl);
}
this.baseUrl = baseUrl;
return this;
}
...
}
注意了Builder给我们提供了三个重载的baseUrl方法,可以传入URL,也在传入一个String,但最终会调用参数为HttpUrl重载方法,也就是说传入的url都会被HttpUrl包装起来。
3.设置数据转换器
设置完了最基本的url,接下来就需要设置我们数据转换器GsonConverterFactory,这也是Retrofit类中官方给处示例的写法。
我们直接看源码:
public Builder addConverterFactory(Converter.Factory factory) {
converterFactories.add(Objects.requireNonNull(factory, "factory == null"));
return this;
}
把传递进来的数据转换器交给了五虎上将之一converterFactories容器缓存起来,GsonConverterFactory它是一个工厂,它不但要负责响应体数据的解析,还要负责请求体的数据转换,所以这个工程提供了两个转换器,如下所示:
public final class GsonConverterFactory extends Converter.Factory {
...
@Override
public Converter<ResponseBody, ?> responseBodyConverter(
Type type, Annotation[] annotations, Retrofit retrofit) {
TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
return new GsonResponseBodyConverter<>(gson, adapter);
}
@Override
public Converter<?, RequestBody> requestBodyConverter(
Type type,
Annotation[] parameterAnnotations,
Annotation[] methodAnnotations,
Retrofit retrofit) {
TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
return new GsonRequestBodyConverter<>(gson, adapter);
}
...
}
GsonResponseBodyConverter负责响应体的数据转换,GsonRequestBodyConverter负责请求体数据转换。
4.正式创建
接下来我们看第四步,建造者通过一系列的构建之后最终会调用build方法来创建最终我们所需要的结果Retrofit。
build方法源码如下:
public Retrofit build() {
//注释1
if (baseUrl == null) {
throw new IllegalStateException("Base URL required.");
}
//注释2
okhttp3.Call.Factory callFactory = this.callFactory;
if (callFactory == null) {
callFactory = new OkHttpClient();
}
//注释3
Executor callbackExecutor = this.callbackExecutor;
if (callbackExecutor == null) {
callbackExecutor = platform.defaultCallbackExecutor();
}
// Make a defensive copy of the adapters and add the default Call adapter.
//注释4
List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor));
// Make a defensive copy of the converters.
//注释5
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.
converterFactories.add(new BuiltInConverters());
converterFactories.addAll(this.converterFactories);
converterFactories.addAll(platform.defaultConverterFactories());
//注释6
return new Retrofit(
callFactory,
baseUrl,
unmodifiableList(converterFactories),
unmodifiableList(callAdapterFactories),
callbackExecutor,
validateEagerly);
}
注释1处如果baseUrl为null那么直接就抛异常了,这也说明了baseUrl在Retrofit创建的时候是必须要设置的,紧接着在注释2处创建了Retrofit背后的大佬OKHttpClient,它是真正执行网络请求的哪个人。接下来的注释3,注释4,注释5都是从platform获取默认的线程切换器MainThreadExecutor,响应适配器DefaultCallAdapterFactory和数据转换器GsonConverterFactory,五虎上将准备完毕之后都交给了注释6处的Retrofit,这样一个Retrofit大元帅就创建完毕了。下面我画了一幅图来总结一下,第一步Retrofit创建的时候都做了哪些操作:
以上就是Retrofit创建的时候所做的事情,这一切都是为请求在做准备工作。
2.服务的创建
Retrofit这个大元帅创建完毕之后,接着需要用它的一个非常重要的方法create,它是真个Retrofit最最最核心的功能,就是用来创建接口服务的,这是我们调用一个网络请求的前提工作。
//2.通过动态代理创建一个用户服务
UserService userService = retrofit.create(UserService.class);
直接进入create方法,一看究竟:
//创建一个接口服务的实现代理对象
public <T> T create(final Class<T> service) {
Log.v("MYTAG","create start...");
//检查传递进来的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 {
Log.v("MYTAG","Proxy invoke start...");
//如果该方法是一个Object的方法则直接就通过反射调用
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
//给方法参数赋值
args = args != null ? args : emptyArgs;
Log.v("isDefaultMethod:",platform.isDefaultMethod(method)+"");
return platform.isDefaultMethod(method)
? platform.invokeDefaultMethod(method, service, proxy, args)
: loadServiceMethod(method).invoke(args);
}
});
}
我们知道一般情况下,声明一个接口之后,需要创建接口的实现类才能使用接口的方法,但是Retrofit大元帅非常牛逼,它打破常规思维,并没有那么干,而是使用Java的一点奇技淫巧,通过Java动态代理的特性来完成了对接口实例的创建,在创建代理对象之前会对接口的合法性进行检查,具体的逻辑如下:
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());
}
//立即校验服务方法的有效性
if (validateEagerly) {
Platform platform = Platform.get();
//获取接口里面的所有方法进行有效性验证
for (Method method : service.getDeclaredMethods()) {
//对非默认方法和非静态方法才进行解析
if (!platform.isDefaultMethod(method) && !Modifier.isStatic(method.getModifiers())) {
loadServiceMethod(method);
}
}
}
}
接口服务,及其方法的合法性检查如下:
- 如果不是接口直接抛异常
- 如果接口有泛型参数则直接抛异常
- 对非默认方法和非静态方法才进行解析
对于第三步要注意,如果需要预解析则提前会解析我们所定义的请求方法,是通过布尔变量validateEagerly来控制的,默认为false
对接口服务合法性检查完毕过后会通过动态代理生成服务代理对象,具体代码实现为:
Proxy.newProxyInstance(service.getClassLoader(),new Class<?>[] {service},new InvocationHandler() {
@Override
public @Nullable Object invoke(Object proxy, Method method, @Nullable Object[] args)throws Throwable {
Log.v("MYTAG","Proxy invoke start...");
}
});
Proxy.newProxyInstance方法这是java给我们提供的,需要传递三个参数,参数1是字节码加载对象,参数2是字节码数组,参数3 InvocationHandler是具体方法在调用时的回调,接口的所有的方法的调用都会走它的invoke方法,关于动态代理的内部实现这块我们不做多讲,这属于Java范畴,在这儿我们不在扩展。这样通过Proxy.newProxyInstance一阵骚操作之后就创建了接口服务对象UserService。
3.创建请求命令
代码分析到这一步,有人可能会问题,Retrofit五虎上将其中四大将都创建了,为何serviceMethodCache一直没有创建?你问对了,这一步就是完成这个工作的, 有了接口服务UserService,我们就可以调用它里面的具体请求方法了,注意了接口方法调用完毕,并没有进行网络请求,而是返回一个网络请求命令Call,有了Call才能进行请求。
//3.创建一个登录命令
Call<BaseResponse> loginCall = userService.loginGet("mxdl", "123456");
这一步的主要工作有两个:
- 对方法进行解析
- 对方法进行适配
方法的解析会生成一个ServiceMethod,方法适配会返回我们的请求命令Call, 当loginGet方法调用时就会走创建动态代理对象时所传入的InvocationHandler对象的invoke方法,InvocationHandler的具体实现如下:
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 {
Log.v("MYTAG","Proxy invoke start...");
//注释1:如果该方法是一个Object的方法则直接就通过反射调用
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
//给方法参数赋值
args = args != null ? args : emptyArgs;
Log.v("isDefaultMethod:",platform.isDefaultMethod(method)+"");
//注释2
return platform.isDefaultMethod(method)
? platform.invokeDefaultMethod(method, service, proxy, args)
: loadServiceMethod(method).invoke(args);
}
}
首先会判断调用的是不是接口服务对象的超类Object所提供的方法,如:toString,equals,wait,notify等,如果是则直接通过反射调用就返回了,否则继续往下走,在注释2处会判断该方法是不是一个默认方法,java8之后允许接口里面通过定义default关键字定义一个有实现逻辑的方法,如果是默认方法则直接走platform平台提供的invokeDefaultMethod调用并直接返回,否则的话就解析我们所定义的标准网络接口方法。
代码分析到这了,就到了Retrofit中最关键的两个步骤,解析方法和适配方法,这也是整个Retrofit框架源码解析逻辑最为复杂的两个步骤。解析是对方法注解的解析落地对象为ServiceMethod,最终会缓存到Retrofit的五虎上将之一serviceMethodCache,他是一个map,具体是由loadServiceMethod操刀完成的。适配是将该方法的落地对象ServiceMethod适配为接口方法的返回对象Call,它具体是由ServiceMethod对象invoke方法来完成的,本质上最终的适配是由我们前面所讲到的Platform所提供的DefaultCallAdapterFactory来操作的,具体细节我们看具体的源码实现。
1.解析方法
//对接口方法进行解析
ServiceMethod<?> loadServiceMethod(Method method) {
Log.v("MYTAG","loadServiceMethod start...");
//从缓存中获取该方法
ServiceMethod<?> result = serviceMethodCache.get(method);
//如果有则直接就返回了
if (result != null) return result;
synchronized (serviceMethodCache) {
result = serviceMethodCache.get(method);
//如果缓存中没有则进行解析
if (result == null) {
//注释1 接口方法解析
result = ServiceMethod.parseAnnotations(this, method);
serviceMethodCache.put(method, result);
}
}
return result;
}
首先会从缓冲中获取,如果缓存有则直接就返回了,否则就会调用注释1处的ServiceMethod.parseAnnotations方法进行解析,解析完毕进行缓存。我们看ServiceMethod.parseAnnotations方法的具体解析实现:
abstract class ServiceMethod<T> {
...
static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
//注释1
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.");
}
//注释2
return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
}
...
}
首先会调用注释1处的RequestFactory.parseAnnotations方法,具体的方法的解析就由这一步来完成的,我们看它的具体实现
具体注解的解析数据就是有RequestFactory对象来存储的httpMethod就是请求的方法类型如get,post等,请求的path由relaviceUrl保存,最重要的是我们的参数都由parameterHandlers来存储,它是个数组,也就是说参数的最终落地对象为ParameterHandler:
以上就是ParameterHandler的大纲视图,他是一个抽象类,它有十几个这样的内部实现类,Path,Fidle,Part等等…,看到这里我们恍然大悟了,所有的注解最终都会被解析为相对应的ParameterHandler对象,这也的确藏的够深的。有了大致的认识之后,我们看RequestFactory.parseAnnotations具体的代码实现:
final class RequestFactory {
static RequestFactory parseAnnotations(Retrofit retrofit, Method method) {
return new Builder(retrofit, method).build();
}
很简单,只有一行代码,有来了一个建造者,我们看具体的build()方法实现:
RequestFactory build() {
//注释1 解析方法的注解,
for (Annotation annotation : methodAnnotations) {
parseMethodAnnotation(annotation);
}
//如果该方法是非get,post等方法则直接抛异常
if (httpMethod == null) {
throw methodError(method, "HTTP method annotation is required (e.g., @GET, @POST, etc.).");
}
if (!hasBody) {
//如果该方法是个文件上传的表单,但是没有请求体则抛异常
if (isMultipart) {
throw methodError(
method,
"Multipart can only be specified on HTTP methods with request body (e.g., @POST).");
}
//如果该方法是个表单,但是没有请求体则抛异常
if (isFormEncoded) {
throw methodError(
method,
"FormUrlEncoded can only be specified on HTTP methods with "
+ "request body (e.g., @POST).");
}
}
int parameterCount = parameterAnnotationsArray.length;
parameterHandlers = new ParameterHandler<?>[parameterCount];
//注释2 参数的解析
for (int p = 0, lastParameter = parameterCount - 1; p < parameterCount; p++) {
parameterHandlers[p] =
parseParameter(p, parameterTypes[p], parameterAnnotationsArray[p], p == lastParameter);
}
//如果没有得到url则抛异常
if (relativeUrl == null && !gotUrl) {
throw methodError(method, "Missing either @%s URL or @Url parameter.", httpMethod);
}
if (!isFormEncoded && !isMultipart && !hasBody && gotBody) {
throw methodError(method, "Non-body HTTP method cannot contain @Body.");
}
if (isFormEncoded && !gotField) {
throw methodError(method, "Form-encoded method must contain at least one @Field.");
}
if (isMultipart && !gotPart) {
throw methodError(method, "Multipart method must contain at least one @Part.");
}
return new RequestFactory(this);
}
//请求类型的解析
private void parseMethodAnnotation(Annotation annotation) {
if (annotation instanceof DELETE) {
parseHttpMethodAndPath("DELETE", ((DELETE) annotation).value(), false);
} else if (annotation instanceof GET) {
parseHttpMethodAndPath("GET", ((GET) annotation).value(), false);
} else if (annotation instanceof HEAD) {
parseHttpMethodAndPath("HEAD", ((HEAD) annotation).value(), false);
} else if (annotation instanceof PATCH) {
parseHttpMethodAndPath("PATCH", ((PATCH) annotation).value(), true);
} else if (annotation instanceof POST) {
parseHttpMethodAndPath("POST", ((POST) annotation).value(), true);
} else if (annotation instanceof PUT) {
parseHttpMethodAndPath("PUT", ((PUT) annotation).value(), true);
} else if (annotation instanceof OPTIONS) {
parseHttpMethodAndPath("OPTIONS", ((OPTIONS) annotation).value(), false);
} else if (annotation instanceof HTTP) {
HTTP http = (HTTP) annotation;
parseHttpMethodAndPath(http.method(), http.path(), http.hasBody());
} else if (annotation instanceof retrofit2.http.Headers) {
String[] headersToParse = ((retrofit2.http.Headers) annotation).value();
if (headersToParse.length == 0) {
throw methodError(method, "@Headers annotation is empty.");
}
headers = parseHeaders(headersToParse);
} else if (annotation instanceof Multipart) {
if (isFormEncoded) {
throw methodError(method, "Only one encoding annotation is allowed.");
}
isMultipart = true;
} else if (annotation instanceof FormUrlEncoded) {
if (isMultipart) {
throw methodError(method, "Only one encoding annotation is allowed.");
}
isFormEncoded = true;
}
}
具体的处理逻辑如下:
- 解析方法的注解
- 如果该方法是非get,post等方法则直接抛异常
- 如果该方法是个文件上传的表单,但是没有请求体则抛异常
- 如果该方法是个表单,但是没有请求体则抛异常
- 参数的解析
- 如果没有得到url则抛异常
- 请求类型的解析
其中核心步骤为第一步和第五步,即注释1处的方法注解的解析和注释2处的参数注解的解析,具体解析逻辑的代码我们就不分析了,我们只要明白最终解析结果会被RequestFactory这个对象保存起来就行了。
方法解析完毕之后生成了一个RequestFactory对象,我们继续往下走就进入了HttpServiceMethod.parseAnnotations方法:
return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
也就是说最终解析的成果RequestFactory会就交给HttpServiceMethod的parseAnnotations方法去处理,HttpServiceMethod类大纲如下:
它是一个抽象类,他有三个具体的实现类:CallAdapted,SuspendForResponse,SuspendForBody,其中后两个是对kotlin协程支持,显然我们这块会返回CallAdapted对象。这几个类他们之间的相互关系如下所示:
具体逻辑我们看代码它的具体实现:
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) {
Type[] parameterTypes = method.getGenericParameterTypes();
Type responseType =
Utils.getParameterLowerBound(
0, (ParameterizedType) parameterTypes[parameterTypes.length - 1]);
if (getRawType(responseType) == Response.class && responseType instanceof ParameterizedType) {
// Unwrap the actual body type from Response<T>.
responseType = Utils.getParameterUpperBound(0, (ParameterizedType) responseType);
continuationWantsResponse = true;
} else {
// TODO figure out if type is nullable or not
// Metadata metadata = method.getDeclaringClass().getAnnotation(Metadata.class)
// Find the entry for method
// Determine if return type is nullable or not
}
adapterType = new Utils.ParameterizedTypeImpl(null, Call.class, responseType);
annotations = SkipCallbackExecutorImpl.ensurePresent(annotations);
} else {
adapterType = method.getGenericReturnType();
}
//注释1
CallAdapter<ResponseT, ReturnT> callAdapter =
createCallAdapter(retrofit, method, adapterType, annotations);
Type responseType = callAdapter.responseType();
if (responseType == okhttp3.Response.class) {
throw methodError(
method,
"'"
+ getRawType(responseType).getName()
+ "' is not a valid response body type. Did you mean ResponseBody?");
}
if (responseType == Response.class) {
throw methodError(method, "Response must include generic type (e.g., Response<String>)");
}
// TODO support Unit for Kotlin?
if (requestFactory.httpMethod.equals("HEAD") && !Void.class.equals(responseType)) {
throw methodError(method, "HEAD method must use Void as response type.");
}
//注释2
Converter<ResponseBody, ResponseT> responseConverter =
createResponseConverter(retrofit, method, responseType);
//注释3
okhttp3.Call.Factory callFactory = retrofit.callFactory;
//注释4
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);
}
}
我们直接看注释4处的关键步骤:
return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);
也就是说在这块,我们前面所做的所有成果都交给了CallAdapted对象,构建它的时候需要传递四个参数:
- requestFactory:RequestFactory,请求需要的参数
- callFactory:OkhttpClient,请求执行器
- responseConverter:GsonConverterFactory,数据转换
- callAdapter:DefaultCallAdapterFactory,请求适配
其中callFactory, responseConverter, callAdapter三大将时候Retrofit提供的,详见注释1,注释2,注释3,而CallAdapted就是我们五大将之一的ServiceMethod,创建完毕会交给交给Retrofit的serviceMethodCache缓存起来。截止目前五大将已经全部创建完毕。
2.方法适配
第一步方法解析是完成了,接下来我们看第二步方法适配,把它要适配成我们的接口方法返回对象Call。
接下来会调用CallAdapted的invoke方法进行适配,参数args就是调用loginGet方法所传入的具体的实参。
@Override
final @Nullable ReturnT invoke(Object[] args) {
Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
return adapt(call, args);
}
各大将和参数都交给了OkHttpCall对象,OkHttpCall就是被背后执行网络请求的那个大佬,它终于现身了,这块会直接调用CallAdapted的adapt方法:
static final class CallAdapted<ResponseT, ReturnT> extends HttpServiceMethod<ResponseT, ReturnT> {
private final CallAdapter<ResponseT, ReturnT> callAdapter;
CallAdapted(
RequestFactory requestFactory,
okhttp3.Call.Factory callFactory,
Converter<ResponseBody, ResponseT> responseConverter,
CallAdapter<ResponseT, ReturnT> callAdapter) {
super(requestFactory, callFactory, responseConverter);
this.callAdapter = callAdapter;
}
@Override
protected ReturnT adapt(Call<ResponseT> call, Object[] args) {
return callAdapter.adapt(call);
}
}
而CallAdapted.adapt方法的内部又会调用callAdapter.adapt方法,callAdapter就是我们Platform所提供的DefaultCallAdapterFactory,接下来我们看DefaultCallAdapterFactory的adapt方法实现:
@Override
public @Nullable CallAdapter<?, ?> get(
Type returnType, Annotation[] annotations, Retrofit retrofit) {
if (getRawType(returnType) != Call.class) {
return null;
}
if (!(returnType instanceof ParameterizedType)) {
throw new IllegalArgumentException(
"Call return type must be parameterized as Call<Foo> or Call<? extends Foo>");
}
final Type responseType = Utils.getParameterUpperBound(0, (ParameterizedType) returnType);
final Executor executor =
Utils.isAnnotationPresent(annotations, SkipCallbackExecutor.class)
? null
: callbackExecutor;
return new CallAdapter<Object, Call<?>>() {
@Override
public Type responseType() {
return responseType;
}
//注释1
@Override
public Call<Object> adapt(Call<Object> call) {
return executor == null ? call : new ExecutorCallbackCall<>(executor, call);
}
};
}
即会调用注释1处的adapt方法,这里传进来的call就是OkHttpCall,显然executor不会为null,那就返回了ExecutorCallbackCall对象,同时把OkHttpCall和executor交给了他,executor就是我们的五大将之一线程切换器,ExecutorCallbackCall就是我们最终要的适配器对象,有了它我们就可以执行一个网络请求命令了。
4.请求的执行
看来ExecutorCallbackCall才是背后大佬的大佬的。它封装了OkHttpCall和callbackExecutor,callbackExecutor就是我们的MainThreadExecutor,他是用来切换线程的。
此时就到了我们请求的最后一步,一切准备就绪,发射:
//4.执行登录命令
loginCall.enqueue(new Callback<BaseResponse>() {
@Override
public void onResponse(Call<BaseResponse> call, Response<BaseResponse> response) {
Log.v("MYTAG", "onResponse start...");
Log.v("MYTAG", response.body().toString());
}
@Override
public void onFailure(Call<BaseResponse> call, Throwable t) {
Log.v("MYTAG", "onFail start...");
Log.v("MYTAG", t.toString());
}
});
loginCall就是我们的ExecutorCallbackCall,我们直接看他的enqueue方法:
static final class ExecutorCallbackCall<T> implements Call<T> {
final Executor callbackExecutor;
final Call<T> delegate;
ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) {
this.callbackExecutor = callbackExecutor;
this.delegate = delegate;
}
//注释1
@Override
public void enqueue(final Callback<T> callback) {
Objects.requireNonNull(callback, "callback == null");
//注释2 执行OkHttpCall的enqueue
delegate.enqueue(
new Callback<T>() {
@Override
public void onResponse(Call<T> call, final Response<T> response) {
//用线程切换器切换线程
callbackExecutor.execute(
() -> {
if (delegate.isCanceled()) {
// Emulate OkHttp's behavior of throwing/delivering an IOException on
// cancellation.
//如果取消则直接调用失败的方法
callback.onFailure(ExecutorCallbackCall.this, new IOException("Canceled"));
} else {
//回调onResponse方法
callback.onResponse(ExecutorCallbackCall.this, response);
}
});
}
@Override
public void onFailure(Call<T> call, final Throwable t) {
//回调onFailure
callbackExecutor.execute(() -> callback.onFailure(ExecutorCallbackCall.this, t));
}
});
}
我们直接看注释2处OkHttpCall的enqueue方法的实现:
@Override
public void enqueue(final Callback<T> callback) {
Objects.requireNonNull(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 {
//注释1
call = rawCall = createRawCall();
} catch (Throwable t) {
throwIfFatal(t);
failure = creationFailure = t;
}
}
}
if (failure != null) {
callback.onFailure(this, failure);
return;
}
if (canceled) {
call.cancel();
}
//注释2
call.enqueue(
new okhttp3.Callback() {
@Override
public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
Response<T> response;
try {
response = parseResponse(rawResponse);
} catch (Throwable e) {
throwIfFatal(e);
callFailure(e);
return;
}
try {
callback.onResponse(OkHttpCall.this, response);
} catch (Throwable t) {
throwIfFatal(t);
t.printStackTrace(); // TODO this is not great
}
}
@Override
public void onFailure(okhttp3.Call call, IOException e) {
callFailure(e);
}
private void callFailure(Throwable e) {
try {
callback.onFailure(OkHttpCall.this, e);
} catch (Throwable t) {
throwIfFatal(t);
t.printStackTrace(); // TODO this is not great
}
}
});
}
不管怎么样,最后都会由OkHttp的Call去完成最终的请求操作详见注释2,程序走到这之后就是OkHttp的事了,这块的逻辑在OkHttp专栏我们已经分析过了,感兴趣的可以去查阅我之前的专栏。
1.Call的创建
现在我们主要看注释1处的createRawCall方法,它创建了OkHttp的Call,有了它就可以发起一个网络请求了,createRawCall方法的具体实现如下:
private okhttp3.Call createRawCall() throws IOException {
okhttp3.Call call = callFactory.newCall(requestFactory.create(args));
if (call == null) {
throw new NullPointerException("Call.Factory returned null.");
}
return call;
}
看到了吗?你让我找的好苦啊,经历了这么多的沟沟坎坎,才看到了我们OkHttp中熟悉的方法:newCall,而requestFactory.create(args)方法不用说返回就是OkHttp中的Request。
final class RequestFactory {
...
okhttp3.Request create(Object[] args) throws IOException {
@SuppressWarnings("unchecked") // It is an error to invoke a method with the wrong arg types.
ParameterHandler<Object>[] handlers = (ParameterHandler<Object>[]) parameterHandlers;
int argumentCount = args.length;
if (argumentCount != handlers.length) {
throw new IllegalArgumentException(
"Argument count ("
+ argumentCount
+ ") doesn't match expected count ("
+ handlers.length
+ ")");
}
RequestBuilder requestBuilder =
new RequestBuilder(
httpMethod,
baseUrl,
relativeUrl,
headers,
contentType,
hasBody,
isFormEncoded,
isMultipart);
if (isKotlinSuspendFunction) {
// The Continuation is the last parameter and the handlers array contains null at that index.
argumentCount--;
}
List<Object> argumentList = new ArrayList<>(argumentCount);
//注释1
for (int p = 0; p < argumentCount; p++) {
argumentList.add(args[p]);
handlers[p].apply(requestBuilder, args[p]);
}
return requestBuilder.get().tag(Invocation.class, new Invocation(method, argumentList)).build();
}
...
}
我们直接看注释1处,会循环我们参数handlers给requestBuilder赋值,handlers就是前面我们在方法解析的时候生成的ParameterHandler,而我们登陆接口的username和password都是Query类型的,我们直接看他的apply方法:
static final class Query<T> extends ParameterHandler<T> {
private final String name;
private final Converter<T, String> valueConverter;
private final boolean encoded;
Query(String name, Converter<T, String> valueConverter, boolean encoded) {
this.name = Objects.requireNonNull(name, "name == null");
this.valueConverter = valueConverter;
this.encoded = encoded;
}
@Override
void apply(RequestBuilder builder, @Nullable T value) throws IOException {
if (value == null) return; // Skip null values.
String queryValue = valueConverter.convert(value);
if (queryValue == null) return; // Skip converted but null values
//注释1
builder.addQueryParam(name, queryValue, encoded);
}
}
看到了吗?注释1处的builder.addQueryParam方法,熟悉配方,熟悉的味道,这就是OkHttp中给url参数赋值的操作。Request被RequestFactory创建好之后,在调用OkHttp的newCall方法,就真正的创建了一个OkHttp的Call对象,有了它就可以发送一个真实的网络请求了,之后的操作流程就进入OkHttp了,关于这部分的源码分析,如果感兴趣可以看我之前所写的OkHttp专栏。
5.小结
Retrofit他就是一个大元帅,统管了五大将,他们各司其职,相互配合,只为一个目标:发起一个网络请求,他们各自在自己平凡的岗位上默默的贡献者自己的力量,姓氏排名不分先后…
- Retrofit:大元帅
- OkHttpClient:请求执行器
- Converter:数据转换器
- CallAdapter:请求适配器
- CallBackExecutor:线程切换器
- ServiceMethod:方法处理器
- RequestFactory:方法解析器
- ParameterHandler:参数处理器
- ExecutorCallbackCall:背后的大佬
- OkHttpCall:背后大佬的大佬
6.学习交流
- 我的公众号:
- Android、Java开发技术交流群
QQ群:810970432
email:geduo_83@163.com