目录
介绍
A type-safe HTTP client for Android and Java.
官方给出的一句话描述:适用于Android和Java的类型安全的HTTP客户端。
是Android工程中使用率很高的网络请求框架。
使用
官方给出的使用样例
public final class SimpleService {
public static final String API_URL = "https://api.github.com";
public static class Contributor {
public final String login;
public final int contributions;
public Contributor(String login, int contributions) {
this.login = login;
this.contributions = contributions;
}
}
public interface GitHub {
// 用注解形式定义接口
@GET("/repos/{owner}/{repo}/contributors")
Call<List<Contributor>> contributors(@Path("owner") String owner, @Path("repo") String repo);
}
public static void main(String... args) throws IOException {
// 构建retrofit实例
// Create a very simple REST adapter which points the GitHub API.
Retrofit retrofit =
new Retrofit.Builder()
.baseUrl(API_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
// 创建接口实例
// Create an instance of our GitHub API interface.
GitHub github = retrofit.create(GitHub.class);
// 获取调用实例
// Create a call instance for looking up Retrofit contributors.
Call<List<Contributor>> call = github.contributors("square", "retrofit");
// 执行请求
// Fetch and print a list of the contributors to the library.
List<Contributor> contributors = call.execute().body();
for (Contributor contributor : contributors) {
System.out.println(contributor.login + " (" + contributor.contributions + ")");
}
}
}
实现的功能是从github仓库中获取项目的贡献者名单,运行结果如下
JakeWharton (1062)
swankjesse (280)
pforhan (48)
eburke (36)
NightlyNexus (29)
dnkoutso (26)
edenman (24)
loganj (17)
Noel-96 (16)
rcdickerson (14)
rjrjr (13)
adriancole (9)
holmes (8)
Jawnnypoo (8)
JayNewstrom (7)
kryali (7)
swanson (7)
crazybob (6)
danrice-square (5)
vanniktech (5)
Turbo87 (5)
naturalwarren (5)
guptasourabh04 (4)
artem-zinnatullin (3)
chriscizek (3)
codebutler (3)
icastell (3)
jjNford (3)
ojh102 (3)
f2prateek (3)
从例子中可以看出,主要进行了如下操作:
- 使用注解定义接口
- 构建Retrofit实例
- 创建接口实例
- 获取调用实例
- 执行请求
有了这个框架很容易就完成了网络请求,这不禁让人有些好奇,这就行了?
- 注解定义的接口是怎么转换成请求的?
- 请求是怎么发出去的?
原理
下面带着疑问尝试把整个流程梳理一下,基于2.9.0版本:
使用注解定义的接口,整个调用过程中一定有对注解的解析,这个暂时不单独特意去看了。
Retrofit构建-Retrofit.Builder().build()
构建Retrofit,从设计模式上看使用了建造者模式,看下build方法中干了什么, 下面代码中只保留了核心代码
public Retrofit build() {
...
okhttp3.Call.Factory callFactory = this.callFactory;
if (callFactory == null) {
callFactory = new OkHttpClient();
}
Executor callbackExecutor = this.callbackExecutor;
if (callbackExecutor == null) {
callbackExecutor = platform.defaultCallbackExecutor();
}
// Make a defensive copy of the adapters and add the default Call adapter.
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.
converterFactories.add(new BuiltInConverters());
converterFactories.addAll(this.converterFactories);
converterFactories.addAll(platform.defaultConverterFactories());
return new Retrofit(
callFactory,
baseUrl,
unmodifiableList(converterFactories),
unmodifiableList(callAdapterFactories),
callbackExecutor,
validateEagerly);
}
其中为构建Retrofit准备了几个参数
参数 | 取值 | 功能 |
---|---|---|
callFactory | 如果调用者有传入则使用传入,否则使用OkHttpClient | 从名称上看这个应该是用来发起http请求调用的工厂对象 |
callbackExecutor | 如果调用者有传入则使用传入,否则使用默认执行器 | 从名称上看是回调使用的执行器 |
callAdapterFactories | 是一个适配器工厂列表,会将传入的添加进去,并且会加入一个默认适配器工厂 | 从名称上看是一组适配器工厂,应该是可以将调用转换为不同形式 |
converterFactories | 是一个转换器工厂列表,会加入一个内置的转换器工厂,加入传入的转换器工厂,再加入默认的转换器工厂 | 从名称上看是一组转换器工厂,应该是用来做数据格式转换的 |
创建接口实例-retrofit.create()
public <T> T create(final Class<T> 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 the method is a method from Object then defer to normal invocation.
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
args = args != null ? args : emptyArgs;
// 判断是否是声明的默认方法(即使用default声明的方法),因为default方法不能使用常规的反射方法调用
return platform.isDefaultMethod(method)
? platform.invokeDefaultMethod(method, service, proxy, args)
: loadServiceMethod(method).invoke(args);
}
});
}
create方法中使用了动态代理模式创建接口实例
获取调用实例-interface.method()
调用接口中方法实际调用的是InvocationHandler#invoke方法,该方法中对接口中声明的不同类型方法使用不同方式进行处理,不过常规情况下走到的几乎都是loadServiceMethod(method).invoke(args),这一句代码中时机包含了两步操作:
- 调用loadServiceMethod获取ServiceMethod实例
- 调用实例的invoke方法
ServiceMethod<?> loadServiceMethod(Method method) {
ServiceMethod<?> result = serviceMethodCache.get(method);
if (result != null) return result;
synchronized (serviceMethodCache) {
// 从缓存中读取ServiceMethod实例
result = serviceMethodCache.get(method);
if (result == null) {
// 缓存中没有的话去创建ServiceMethod
result = ServiceMethod.parseAnnotations(this, method);
serviceMethodCache.put(method, result);
}
}
return result;
}
创建ServiceMethod其中又包含了两步:
static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
// 1、生成RequestFactory实例
RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);
...
// 2、生成ServiceMethod实例
return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
}
生成RequestFactory是通过调用RequestFactory$Builder.build实现的,表面看是生成RequestFactory实例,实际上该方法会解析方法注解和参数注解
- 解析方法注解:得到url路径、拿到路径中的参数名(如{owner})
- 解析参数注解:将参数注解都转换为ParameterHandler对象,每个对象都会实现apply方法,在这个方法中,会调RequestBuilder对应的方法,用以向请求中添加参数,利用了装饰者模式。例如,ParameterHandler.Query#apply中调用了RequestBuilder#addQueryParam方法;ParameterHandler.Path#apply中调用了RequestBuilder#addPathParam方法。
生成ServiceMethod同样也没有那么简单
- 获取CallAdapter:这里拿到的是按序遍历callAdapterFactories(构建Retrofit是被赋值)列表中的工厂创建的adapter,其中第一个返回类型和要求相符的adapter
- 获取Converter:这里拿到的是按序遍历converterFactories(构建Retrofit是被赋值)列表中的工厂创建的converter中第一个符合要求的
- 获取callFactory:这里拿到的是callFactory(构建Retrofit是被赋值)
- 创建HttpServiceMethod:利用上面获取到的三个对象,加上传入的RequestFactory对象,去完成创建,不考虑kotlin的情况下,创建出来的对象是CallAdapted(继承关系CallAdapted -> HttpServiceMethod -> ServiceMethod)实例
上面说过loadServiceMethod(method).invoke(args)是调用ServiceMethod#invoke方法,invoke方法在HttpServiceMethod中被重写,并调用了抽象方法adapt
@Override
final @Nullable ReturnT invoke(Object[] args) {
// 这里创建的OkHttpCall是重点关注对象
Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
return adapt(call, args);
}
而adapt又在CallAdapted中被重写, 并调用callAdapter.adapt方法,callAdapter的来源在生成ServiceMethod的过程中已经提到了
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);
}
}
那么loadServiceMethod(method).invoke(args)实际调用的是callAdapter.adapt(call),这里使用了适配器模式
在构建Retrofit时如果没有传入默认使用的是DefaultCallAdapterFactory创建的callAdapter
final class DefaultCallAdapterFactory extends CallAdapter.Factory {
private final @Nullable Executor callbackExecutor;
DefaultCallAdapterFactory(@Nullable Executor callbackExecutor) {
this.callbackExecutor = callbackExecutor;
}
// 调用get方法用来创建CallAdapter
@Override
public @Nullable CallAdapter<?, ?> get(
Type returnType, Annotation[] annotations, Retrofit retrofit) {
...
// 注解中如果不包含@SkipCallbackExecutor则使用传入的callbackExecutor
final Executor executor =
Utils.isAnnotationPresent(annotations, SkipCallbackExecutor.class)
? null
: callbackExecutor;
return new CallAdapter<Object, Call<?>>() {
@Override
public Type responseType() {
return responseType;
}
@Override
public Call<Object> adapt(Call<Object> call) {
// 注解包含@SkipCallbackExecutor使用传入的call(即OkHttpCall),否则使用ExecutorCallbackCall
return executor == null ? call : new ExecutorCallbackCall<>(executor, call);
}
};
}
...
}
ExecutorCallbackCall使用了代理模式,对call进行代理,在call的成功、失败方法中加入对executor的回调,这种方式可用来切换线程,到这里可以知道得到的call
实际就是OkHttpCall
执行请求-call.execute()
注意:到这里为止的Call都是Retrofit中的Call类型(retrofit2.Call),下面还会涉及到okhttp3.Call
public Response<T> execute() throws IOException {
okhttp3.Call call;
synchronized (this) {
if (executed) throw new IllegalStateException("Already executed.");
executed = true;
// 得到原始请求调用
call = getRawCall();
}
if (canceled) {
call.cancel();
}
// 解析响应内容
return parseResponse(call.execute());
}
依然可以分成两部分
- 获得原始请求调用
- 执行请求
- 解析响应内容
原始请求构造
OkHttpCall中构造原始请求调用的方法如下
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;
}
callFactory是在Retrofit.Builder().build()时被赋值的,默认是okhttp3.OkHttpClient, 是okhttp3中的一个类,内部逻辑暂且不看,知道newCall是用来生成okhttp3.Call即可,并且需要传入一个okhttp3.Request类型的参数,这个参数是通过RequestFactory#create得到的
okhttp3.Request create(Object[] args) throws IOException {
@SuppressWarnings("unchecked") // It is an error to invoke a method with the wrong arg types.
// 前面提到过ParameterHandler是解析注解后得到的参数对象
ParameterHandler<Object>[] handlers = (ParameterHandler<Object>[]) parameterHandlers;
...
RequestBuilder requestBuilder =
new RequestBuilder(
httpMethod,
baseUrl,
relativeUrl,
headers,
contentType,
hasBody,
isFormEncoded,
isMultipart);
...
List<Object> argumentList = new ArrayList<>(argumentCount);
for (int p = 0; p < argumentCount; p++) {
argumentList.add(args[p]);
// 调用applay会把相应的请求参数添加到requestBuilder中
handlers[p].apply(requestBuilder, args[p]);
}
// 构造一个http请求
return requestBuilder.get().tag(Invocation.class, new Invocation(method, argumentList)).build();
}
过程中会把解析注解得到的参数添加到request中,并将构造的request返回
执行请求
执行逻辑同样在okhttp3中,调用的是okhttp3.Call#execute,不过okhttp3.Call是个接口,实现类是okhttp3.RealCall
解析响应内容
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);
}
ExceptionCatchingResponseBody catchingBody = new ExceptionCatchingResponseBody(rawBody);
try {
// 使用构造Retrofit时设置的转换器进行数据转换
T body = responseConverter.convert(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;
}
}
过程分为三步:
- 根据返回码判断请求是否成功
- 进行数据格式转换(200<=code<=203才会执行)
- 根据响应内容构造一个响应对象并返回
梳理下整个流程的调用关系