Retrofit 2.3.0 源码解析
前言
Retrofit A type-safe HTTP client for Android and Java
Retrofit,是一个基于http请求库二次封装的HTTP客户端,将 REST API 转换为 Java 接口。
基于注解,进一步解放了生产力,使得http请求就像调用方法一样简单,如丝般顺滑。
结构概览
architecture.png项目结构整体分四个部分,Builder -> Proxy -> Invocation -> RawCall
这里我们把基于Retrofit的HTTP通信比做是邮递信件。
邮递信件
- 信封:当我们准备好信件之后,要在信封上写邮寄地址,收件人,可能还要备注勿折(是的,我暴露了我的年龄,如今很多人可能都没有过写信寄信的体验)。
- 邮递员:然后我们亲自去送信吗?No,我们把信投入邮箱,交给邮递员代为送信就行了。
- 邮局:然后邮递员会根据信封上的信息对信件进行分拣,寄信或收信均经由邮局统一处理
- 邮寄方式:最后就是交给运送单位送信了,空运或是陆运等。
基于Retrofit的HTTP通信
- Builder:当我们准备好数据之后,要指定服务端的通信地址,处理接口地址,请求方法,可能还要备注是否有body、是否是multipart。
- Proxy:然后通信的事交给代理去做,代理会帮你做好一系列的工作,比如注解解析,Call适配,以及请求调度等
- Invocation:这里负责调度同步或异步请求,请求装配和响应解析
- RawCall:这里就是具体的通信工具了,可选Okhttp等框架来做具体的Http通信。
来看看寄信和Retrofit之间的对比:
arch_flow.png大概过程就是这样,邮递员会把信送出去,并在适合的时机把对方的回信取回来送给你,当然如果你的信件是表白情书,那也很可能会收不到回信的,毕竟表白成功的概率要看人品的。不要伤心,HTTP通信也会有时候收不到服务端的回信噢。
目录概览
│ BuiltInConverters.java # 内建Converter
│ Call.java # 发送请求接收响应的retrofit方法调用
│ CallAdapter.java # 适配Call的响应类型,将默认响应类型R转换为类型T
│ Callback.java # 返回服务端或离线请求的响应体
│ Converter.java # HTTP交互中,转换对象为数据 或 从数据转换为对象
│ DefaultCallAdapterFactory.java # 默认CallAdapter工厂
│ ExecutorCallAdapterFactory.java # http请求执行器工厂
│ HttpException.java # 非2xx HTTP响应的异常处理
│ OkHttpCall.java # 真正调用OkHttp3发送Http请求的类
│ package-info.java # 包描述
│ ParameterHandler.java # 参数注解解析器
│ Platform.java # 平台适配(Java/Android)
│ RequestBuilder.java # 请求拼装
│ Response.java # 原汁原味的HTTP 响应体,所谓 T body
│ Retrofit.java # 组装工厂,基于建造者模式拼装自定义HTTP交互所需的组件,并作为总调度暴露接口
│ ServiceMethod.java # 框架核心处理类,注解解析器调度,生成请求(包含api url、path、http请求方法、请
# 求头、是否是multipart等等),并返回用于发起http请求的Call对象
│ Utils.java # 工具类
│
└─http # http注解定义 (直接引用了Javadoc中的描述,均为提高生产力的注解)
Body.java # control the request body of a POST/PUT request
DELETE.java # Make a DELETE request
Field.java # Named pair for a form-encoded request
FieldMap.java # Named key/value pairs for a form-encoded request
FormUrlEncoded.java # Denotes that the request body will use form URL encoding
GET.java # Make a GET request
HEAD.java # Make a HEAD request
Header.java # Replaces the header with the value of its target
HeaderMap.java # Adds headers specified in the Map
Headers.java # Adds headers literally supplied in the value
HTTP.java # Use a custom HTTP verb for a request
Multipart.java # Denotes that the request body is multi-part
OPTIONS.java # Make an OPTIONS request
package-info.java # Package description
Part.java # Denotes a single part of a multi-part request
PartMap.java # Denotes name and value parts of a multi-part request
PATCH.java # Make a PATCH request
Path.java # Named replacement in a URL path segment
POST.java # Make a POST request
PUT.java # Make a PUT request
Query.java # Query parameter appended to the URL
QueryMap.java # Query parameter keys and values appended to the URL
QueryName.java # Query parameter appended to the URL that has no value
Streaming.java # Treat the response body on methods returning Response as is, i.e.
# without converting body() to byte[]
Url.java # URL resolved against the base URL
Retrofit的基本用法
让我们从基本用法开始,先看如何使用,顺着这个藤,摸摸如何实现的瓜。
用 Java 接口的方式定义一个HTTP API.
public interface GitHubService {
@GET("users/{user}/repos")
Call<List<Repo>> listRepos(@Path("user") String user);
}
Retrofit 类生成一个 GitHubService 接口的实现实例.
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.build();
GitHubService service = retrofit.create(GitHubService.class);
Each Call from the created GitHubService can make a synchronous or asynchronous HTTP request to the remote webserver.
GitHubService实例的每一个方法调用都支持同步或异步HTTP请求.
Call<List<Repo>> repos = service.listRepos("octocat");
执行同步或异步HTTP请求,得到HTTP响应数据.
Response<List<Repo>> response = repos.execute();
Retrofit的源码解析
首先我们心里要有个概念,Retrofit的核心关键词:注解、动态代理、转换器、适配器
Retrofit就是基于这四个关键词搭建起来的充分解耦,灵活,可插拔的优秀框架。
下面我们结合Retrofit设计图流程来解读代码。 还记得流程吗? Builder -> Proxy -> Invocation -> RawCall.
Flow - Builder
Retrofit.Builder() .baseUrl("https://api.github.com/") ... .build();
Tips.设计模式之Builder模式
基于Builder模式,装配一系列零部件,比如base请求地址,gson转换器,Rxjava适配器,HTTP请求client(比如装配OKHTTP)等。
// Retrofit.java -> class Builder
public Retrofit build() {
...
return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
callbackExecutor, validateEagerly);
}
返回一个装配了 callFactory,converterFactories,adapterFactories,callbackExecutor 和指定了 baseUrl 的 Retrofit 实例。
注:validateEagerly
,用于指定是否预先解析注解,加速接口访问效率。
Flow - Proxy
GitHubService service = retrofit.create(GitHubService.class);
我们知道,Java 接口是不可以直接 new 实例的,那么这个 create 方法看起来又像是返回了一个 GitHubService 接口类型的实现实例,这是怎么回事呢?我们来看下 create 的实现。
// Retrofit.java
public <T> T create(final Class<T> service) {
Utils.validateServiceInterface(service);
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 {
// If the method is a method from Object then defer to normal invocation.
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
if (platform.isDefaultMethod(method)) {
return platform.invokeDefaultMethod(method, service, proxy, args);
}
ServiceMethod<Object, Object> serviceMethod =
(ServiceMethod<Object, Object>) loadServiceMethod(method);
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
}
});
}
create方法主要就一个return,返回了一个Proxy.newProxyInstance生成的动态代理对象。原来这里是通过动态代理的方式生成了 GitHubService 接口的代理实例,那么后续 GitHubService 接口的方法都可以通过代理去调用了。
为什么用动态代理?
这是Retrofit设计的核心思路,基于动态代理,可以为后续在调用 GitHubService 接口的相关方法时先拦截下来,做完一系列工作后(即注解解析,请求转换,适配等),再去完成方法本尊想要完成的工作,这就是动态代理的魅力。
Tips.动态代理
Call<List<Repo>> repos = service.listRepos("octocat");
通过代理对象 service 调用接口方法 listRepos ,会被动态代理拦截,调用Proxy.newProxyInstance方法中的InvocationHandler对象的 invoke 方法。
invoke中主要由ServiceMethod和CallAdapter完成了三件事:
- 请求方法的注解解析
- 创建OkHttpCall实例,为后续流程中的HTTP请求执行做准备,详见 Flow - Invocation.
- 适配Call的响应类型,将默认响应类型R转换为类型T
ServiceMethod<Object, Object> serviceMethod =
(ServiceMethod<Object, Object>) loadServiceMethod(method);
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
ServiceMethod.java
// ServiceMethod.java
public ServiceMethod build() {
callAdapter = createCallAdapter();
responseType = callAdapter.responseType();
...
responseConverter = createResponseConverter();
for (Annotation annotation : methodAnnotations) {
parseMethodAnnotation(annotation);
}
...
int parameterCount = parameterAnnotationsArray.length;
parameterHandlers = new ParameterHandler<?>[parameterCount];
for (int p = 0; p < parameterCount; p++) {
Type parameterType = parameterTypes[p];
...
Annotation[] parameterAnnotations = parameterAnnotationsArray[p];
...
parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);
}
...
return new ServiceMethod<>(this);
}
获取callAdapter、responseType、responseConverter接口对象
解析Method的注解
解析Method的参数注解
解析Method的参数中使用了依赖请求API的动态参数的注解,交由ParameterHandler处理
CallAdapter.java
public interface CallAdapter<R, T> {
Type responseType();
...
T adapt(Call<R> call);
...
}
适配Call的响应类型,将默认响应类型R转换为类型T.比如官方的RxJavaCallAdapter可以结合Rxjava特性对Call的响应做RxJava观察者模式转换,进一步解放生产力。
注:未在Builder阶段指定CallAdapter(如 RxJavaCallAdapterFactory )的情况下,默认的 CallAdapter 不对Call做任何处理。
见 DefaultCallAdapterFactory:
final class DefaultCallAdapterFactory extends CallAdapter.Factory {
static final CallAdapter.Factory INSTANCE = new DefaultCallAdapterFactory();
...
...
@Override public Call<Object> adapt(Call<Object> call) {
return call;
}
}
}
Flow - Invocation
Response<List<Repo>> response = repos.execute();
这一步开始基于同步的方式执行HTTP请求,并得到返回的HTTP响应数据.
本质上是执行了 OkHttpCall 的 execute方法.
// OkHttpCall.java
@Override public Response<T> execute() throws IOException {
synchronized (this) {
...
...
call = rawCall = createRawCall();
}
...
return parseResponse(call.execute());
}
如你所见,这里创建了RawCall,即真正的去执行HTTP请求任务的对象。
这里还负责HTTP请求的响应数据解析。
我们看下createRawCall()
干了什么。
// OkHttpCall.java
private okhttp3.Call createRawCall() throws IOException {
Request request = serviceMethod.toRequest(args);
okhttp3.Call call = serviceMethod.callFactory.newCall(request);
...
return call;
}
serviceMethod.toRequest()的功能:
// ServiceMethod.java
/** Builds an HTTP request from method arguments. */
Request toRequest(@Nullable Object... args) throws IOException {
RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl, headers,
contentType, hasBody, isFormEncoded, isMultipart);
...
return requestBuilder.build();
}
toRequest 方法通过 RequestBuilder 创建了 okhttp3 做 HTTP 请求时需要的 Request 对象。
serviceMethod.callFactory.newCall(request)的功能:
建立一个请求通道,为执行HTTP请求做准备。
这里callFactory可以由使用者指定,默认为 OkHttpClient,见:
// Retrofit.java
okhttp3.Call.Factory callFactory = this.callFactory;
if (callFactory == null) {
callFactory = new OkHttpClient();
}
回头看下 OkHttpCall 中 execute 方法最后一句: return parseResponse(call.execute());
这里调用真正的HTTP请求客户端的请求执行方法。也就是来到了接下来的一个流程。
Flow - RawCall
上个 Flow 中最后一步, call.execute()
,开启了真正的HTTP请求,即通过 okhttp3 完成HTTP请求。
这个部分没什么代码可讲,属于面向接口开发的典范,要讲就该去讲 Okhttp 框架的源码了。
这个部分引出了 Retrofit 的开源拥有者-Square 公司的另一个优秀的开源项目 Okhttp,是不是也很想一探究竟?