原载于个人博客Retrofit源码探秘, 转载请注明出处, 谢谢.
本文讲Retrofit. 总的来说它是一个面向业务流程的网络请求库, 内部默认使用面向网络协议的库OkHttp来打出成吨伤害.
截至我写文章为止, Retrofit
更新到了2.2.0
版本. 据说1.x
与2.0+
差异巨大, 这里我们就直接拥抱变化来看最新代码了.
Retrofit的使用
添加依赖和添加网络权限我就不说了. 假设我们要访问网络获取一个Json
格式的响应, 来看代码怎么写:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://url.of.yourserver/")
.addConverterFactory(GsonConverterFactory.create())
.build(); // 创建全局管理
HttpService httpService = retrofit.create(YourHttpService.class); // 创建访问接口实例
Call<Json> call = httpService.getJson(); // 为要访问的接口创建Call
call.enqueue(new YourCallbackForThisCall(){...}); // Call入队列并添加回调
非常简单对不对, 而且YourHttpService.class
甚至可能根据某种规范直接从服务端导出, 需要自己手写的代码几乎可以忽略不计, 出bug的几率就更小了.
这里的Callback
经我验证是在UI线程执行的, 所以可以直接在onResponse
里更新UI. 如果想要换到其他线程处理, 需要设置callbackExecutor
, 默认是主线程.
Retrofit源码探秘
根据我们前面分析过的OkHttp
源码可以知道, Retrofit
背后的请求发送和接收都是通过它来完成的, 自己几乎没做什么. Retrofit
能实现这么简单的调用方式, 其魔法就在它的抽象架构上. Retrofit
通过定义和解析注解的方式生成Request
, 又在原本OkHttpCall
(Retrofit
定义的OkHttp
的代理)的回调之中加了一层, 用于处理响应出错情况和把响应结果包装成ExceptionCatchingRequestBody
, 最后用ServiceMethod.toResponse
使用Converter
完成响应解析, 使用解析结果通过CallAdapter
构造一个自定义的Response<T>
并返回. 再在外层回调我们设置的Callback<T>
.
真正的网络请求的事情包括连接和缓存的维护都交给OkHttpClient
完成了, 因此关键就在于Retrofit
从注解到Call
的过程和从Response
到Response<T>
的过程.
Retrofit注解定义和解析
在我之前的某篇文章中曾经解析过Java
的Annotation
机制, 这里就不再解释了, 我们已经知道, Java
的注解是通过注解处理器Processor
进行处理的, Retrofit
应该也是实现了类似的机制去处理它自己的注解. 注解都是编译器完成处理的, 其处理后的代码的生命周期根据它的Retention
不同而不同, Retention
为RUNTIME
的注解在编译器处理之后存在于.class
文件中并且还被虚拟机载入, 所以可供后续处理. 接下来我们就来看看Retrofit
中的注解定义和解析器的定义, 我们将会发现它所有的注解都是RUNTIME
的.
注解都在http
包下, 从名字也看得出来基本上对应了http
协议中的各个实体. 我们只看其中GET
Headers
Streaming
三个.
选这三个的原因是三个在http
协议中分别代表三种类型: 请求方法, 结构参数, 传输标记. 如果不记得http
协议的同学可以看我另一篇博客Android源码解析-网络架构.
三个注解的完整代码如下:
@Target(METHOD)
@Retention(RUNTIME)
public @interface GET {
/**
* A relative or absolute path, or full URL of the endpoint. This value is optional if the first
* parameter of the method is annotated with {@link Url @Url}.
* <p>
* See {@linkplain retrofit2.Retrofit.Builder#baseUrl(HttpUrl) base URL} for details of how
* this is resolved against a base URL to create the full endpoint URL.
*/
String value() default "";
}
/**
* Adds headers literally supplied in the {@code value}.
* <pre><code>
* @Headers("Cache-Control: max-age=640000")
* @GET("/")
* ...
*
* @Headers({
* "X-Foo: Bar",
* "X-Ping: Pong"
* })
* @GET("/")
* ...
* </code></pre>
* <strong>Note:</strong> Headers do not overwrite each other. All headers with the same name will
* be included in the request.
*
* @see Header
* @see HeaderMap
*/
@Documented
@Target(METHOD)
@Retention(RUNTIME)
public @interface Headers {
String[] value();
}
/**
* Treat the response body on methods returning {@link okhttp3.Response Response} as is,
* i.e. without converting {@link okhttp3.Response#body() body()} to {@code byte[]}.
*/
@Documented
@Target(METHOD)
@Retention(RUNTIME)
public @interface Streaming {
}
为避免各位说我凑篇幅, 请各位仔细看上文代码的注释部分和注解部分, 我们可以看到这几个注解的设计思路, 基本上是按照http
协议的基本部分来分解的, 并且都有明确的作用对象和作用范围.
对这三种类型的注解的处理自然也是不太一样, 接下来我们就看看Retrofit
为它的这些注解编写了怎样的处理器. 注解的转存在ServiceMethod.Builder()
中, 处理则在ServiceMethod.Builder.build()
中, 如下:
public ServiceMethod build() {
...
// Method级别的Annotation, 主要是GET/POST等方法
for (Annotation annotation : methodAnnotations) {
parseMethodAnnotation(annotation);
}
...
// Parameter级别的Annotation, 主要是Path等
int parameterCount = parameterAnnotationsArray.length;
parameterHandlers = new ParameterHandler<?>[parameterCount];
for (int p = 0; p < parameterCount; p++) {
Type parameterType = parameterTypes[p];
if (Utils.hasUnresolvableType(parameterType)) {
throw parameterError(p, "Parameter type must not include a type variable or wildcard: %s",
parameterType);
}
Annotation[] parameterAnnotations = parameterAnnotationsArray[p];
if (parameterAnnotations == null) {
throw parameterError(p, "No Retrofit annotation found.");
}
parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);
}
...
}
// 处理Method级别Annotation
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);
if (!Void.class.equals(responseType)) {
throw methodError("HEAD method must use Void as response type.");
}
} 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("@Headers annotation is empty.");
}
headers = parseHeaders(headersToParse);
} else if (annotation instanceof Multipart) {
if (isFormEncoded) {
throw methodError("Only one encoding annotation is allowed.");
}
isMultipart = true;
} else if (annotation instanceof FormUrlEncoded) {
if (isMultipart) {
throw methodError("Only one encoding annotation is allowed.");
}
isFormEncoded = true;
}
}
// 处理Parameter级别Annotation
private ParameterHandler<?> parseParameter(
int p, Type parameterType, Annotation[] annotations) {
ParameterHandler<?> result = null;
for (Annotation annotation : annotations) {
ParameterHandler<?> annotationAction = parseParameterAnnotation(
p, parameterType, annotations, annotation);
...// handleException
result = annotationAction;
}
...// handleException
return result;
}
// 具体的处理Annotation的实现, 方法颇长就不贴详情了
private ParameterHandler<?> parseParameterAnnotation(
int p, Type type, Annotation[] annotations, Annotation annotation) {
if (annotation instanceof Url) { // handle @Url
...// throw Exception if necessary
gotUrl = true;
if (type == HttpUrl.class
|| type == String.class
|| type == URI.class
|| (type instanceof Class && "android.net.Uri".equals(((Class<?>) type).getName()))) {
return new ParameterHandler.RelativeUrl();
} else {
...// throw Exception
}
} else if (annotation instanceof Path) { // handle @Path
...// throw Exception if necessary
gotPath = true;
Path path = (Path) annotation;
String name = path.value();
validatePathName(p, name);
Converter<?, String> converter = retrofit.stringConverter(type, annotations); // 见Retrofit.stringConverter()
return new ParameterHandler.Path<>(name, converter, path.encoded()); // 见ParameterHandler.Path<>
} else if (annotation instanceof Query) { // handle @Query
} else if (annotation instanceof QueryName) { // handle @QueryName
} else if (annotation instanceof Header) { // handle @Header
} else if (annotation instanceof FieldMap) { // handle @FieldMap
} else if (annotation instanceof Part) { // handle @Part
} else if (annotation instanceof PartMap) { // handle @PartMap
} else if (annotation instanceof Body) { // handle @Body
}
return null; // Not a Retrofit annotation.
}
可见Retrofit
对注解的处理分为两个部分: 一个是Method
级别的Annotation
, 所有http
协议方法都被定义为该级别的Annotation
, 在ServiceMethod.parseHttpMethodAndPath
中处理(注意到, 基本方法DELETE
GET
HEAD
POST
等应该是互斥的, 但代码中没有进行判断, 而Multipard
和FormUrlEncoded
之间却进行了判断, 至于代码中没有对Streaming
的处理, 是因为逻辑上它不在这一层, 而是在响应结果的解析, 所以其实是在retrofit2/BuiltInConverters.java
中进行了处理); 另一个是Parameter
级别的, 大多数是在进行基本预处理之后就委托给ParameterHandler
接口的实现类进行处理.
通过对某个方法的注解的处理, 在ServiceMethod.Builder.build()
完成后ServiceMethod
就获取到了调用该方法发起网络请求所需要的信息, 可以开始构造Call
了.
Retrofit的Call与Response
Retrofit
的Call
和Response
是对OkHttp
的Call
和Response
的封装, 为什么要进行这么一层封装呢? 接下来我们就看看它的源码.
我们选择从核心方法Retrofit.create(Class)
开始.
public <T> T create(final Class<T> service) {
Utils.validateServiceInterface(service); // #1
if (validateEagerly) {
eagerlyValidateMethods(service); // #2
}
// #3 below
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, 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); // #4
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args); // #5
return serviceMethod.callAdapter.adapt(okHttpCall); // #6
}
});
}
这里面一共有三个步骤, 其中前两步是用来检查接口和提前载入方法的, 第三步是关键, 生成并返回了一个代理. 实例化代理时传入了三个参数, 第三个是InvocationHandler
的实现, 我们可以看到里面最关键的步骤是#4 #5 #6
, 分别完成方法载入(Method->ServiceMethod
), Call
构造和返回Call
代理. 这里Proxy
及InvocationHandler
都是Java
中就已经存在并引入了Android
中的机制, 即动态代理机制. 关于动态代理机制, 思想成熟战法犀利, 是相当有意思的一个东西, 可以参见这篇文章
总之要使用动态代理呢, 委托类必须要实现某个接口, 而Proxy
创建的代理类通过实现InvocationHandler
来调用委托类的方法, 完成代理. 所有这一切都跟反射密不可分, 所以理所当然Proxy
是在反射包下的.
从上面代码可以看出, Platform.isDefaultMethod()
和Object
的方法将被InvocationHandler
直接代理执行, 而其他方法则被包装成OkHttpCall
交给ServiceMethod
的CallAdapter
去执行. 这里也可以看出对Call
和Response
进行封装代理的作用: 屏蔽底层实现, 统一处理非核心逻辑, 统一接口, 令底层替换成其他网络访问库成为可能.
下面我们来看ServiceMethod
的构造过程, OkHttpCall
的构造过程以及默认的CallAdapter
的代码.
还记得在create
里传入loadServiceMethod
的是Method method
, 它是代理方法, 通过它去访问委托(被代理)方法, 代码如下:
ServiceMethod<?, ?> loadServiceMethod(Method method) {
ServiceMethod<?, ?> result = serviceMethodCache.get(method);
if (result != null) return result;
synchronized (serviceMethodCache) {
result = serviceMethodCache.get(method);
if (result == null) {
result = new ServiceMethod.Builder<>(this, method).build(); // 传入Retrofit, Method实例, 构造代理包装类ServiceMethod实例
serviceMethodCache.put(method, result); // 缓存代理方法-代理包装实例映射
}
}
return result;
}
然后根据传入的方法及其参数, 构造一个合适的Call
, 这个Call
是OkHttp
中Call
的代理, 所以Retrofit
中它的类是OkHttpCall
. 下面我们看它是怎么构造和代理的, 代码如下:
final class OkHttpCall<T> implements Call<T> {
OkHttpCall(ServiceMethod<T, ?> serviceMethod, Object[] args) {
this.serviceMethod = serviceMethod;
this.args = args;
}
@Override public void enqueue(final Callback<T> callback) {
// 检查参数
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 {
call = rawCall = createRawCall();
} catch (Throwable t) {
failure = creationFailure = t;
}
}
}
if (failure != null) {
callback.onFailure(this, failure);
return;
}
if (canceled) {
call.cancel();
}
call.enqueue(new okhttp3.Callback() { // okhttp3.Callback反向代理retrofit2.Callback
@Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse)
throws IOException {
Response<T> response;
try {
response = parseResponse(rawResponse); // 解析响应
} catch (Throwable e) {
callFailure(e); // 回调自身callFailure
return;
}
callSuccess(response); // 回调自身callSuccess
}
@Override public void onFailure(okhttp3.Call call, IOException e) {
try {
callback.onFailure(OkHttpCall.this, e);
} catch (Throwable t) {
t.printStackTrace();
}
}
private void callFailure(Throwable e) {
try {
callback.onFailure(OkHttpCall.this, e);
} catch (Throwable t) {
t.printStackTrace();
}
}
private void callSuccess(Response<T> response) {
try {
callback.onResponse(OkHttpCall.this, response);
} catch (Throwable t) {
t.printStackTrace();
}
}
});
}
}
结构清晰, 基本可以当做OkHttpCall
来对待.
接下来通过ServiceMethod.callAdapter.adapt(OkHttpCall)
将OkHttpCall
转换为Call<T>
. ServiceMethod
的建造模式如下:
Builder(Retrofit retrofit, Method method) {
this.retrofit = retrofit;
this.method = method;
this.methodAnnotations = method.getAnnotations(); # 获取代理方法的方法注解
this.parameterTypes = method.getGenericParameterTypes(); # 获取泛型参数类型
this.parameterAnnotationsArray = method.getParameterAnnotations(); # 获取参数注解
}
public ServiceMethod build() {
callAdapter = createCallAdapter(); # 根据泛型返回类型和方法注解, 通过retrofit.callAdapter(returnType, annotations)寻找适配器实例. Retrofit实例化时默认添加`DefaultCallAdapterFactory`工厂
responseType = callAdapter.responseType(); # 获取返回类型
if (responseType == Response.class || responseType == okhttp3.Response.class) {
throw methodError("'"
+ Utils.getRawType(responseType).getName()
+ "' is not a valid response body type. Did you mean ResponseBody?"); # 不允许以`Response`作为返回类型
}
responseConverter = createResponseConverter();
for (Annotation annotation : methodAnnotations) {
parseMethodAnnotation(annotation); # 解析方法注解
}
if (httpMethod == null) {
throw methodError("HTTP method annotation is required (e.g., @GET, @POST, etc.).");
}
...// 判断方法注解合法性
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);
}
ServiceMethod(Builder<R, T> builder) {
this.callFactory = builder.retrofit.callFactory();
this.callAdapter = builder.callAdapter;
this.baseUrl = builder.retrofit.baseUrl();
this.responseConverter = builder.responseConverter;
this.httpMethod = builder.httpMethod;
this.relativeUrl = builder.relativeUrl;
this.headers = builder.headers;
this.contentType = builder.contentType;
this.hasBody = builder.hasBody;
this.isFormEncoded = builder.isFormEncoded;
this.isMultipart = builder.isMultipart;
this.parameterHandlers = builder.parameterHandlers;
}
始终别忘了以上内容都是通过Java
自带的Proxy
机制调用InvocationHandler
来代理的, 所以create
传入的是什么类, 返回的就是什么类的代理, 访问这个代理的方法时, 实际调用的是InvocationHandler
. 通过ServiceMethod.callAdapter.adapt()
可以将传入的方法适配为你自己定义的返回类型Call<T>
的实例. 我们来看看默认情况下添加的DefaultCallAdapterFactory
会生成什么CallAdapter
:
public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) { // 工厂方法, 传入返回类型, 注解数组, retrofit实例
if (getRawType(returnType) != Call.class) { // 返回类型如果不是Call则直接返回null
return null;
}
final Type responseType = Utils.getCallResponseType(returnType); // 获取ReturnType<T>中泛型参数T的上限类型
return new CallAdapter<Object, Call<?>>() {
@Override public Type responseType() {
return responseType;
}
@Override public Call<Object> adapt(Call<Object> call) {
return call; // 默认返回传入参数
}
};
}
显然默认情况下这个适配器什么也不做, 直接把传入的OkHttpCall
返回给你.
Retrofit的Converter与CallAdapter: 开始使用RxJava
上文已经暗示了, 对结果的解析器Converter
可以替换, 其实发送请求的代理Call
也可以被替换, 只要换掉CallAdapter
就可以了, 网上都说RxJava+Retrofit
可以打出成吨输出, 原因就在于CallAdapter
可以换成一个返回Observable
类型的适配器, 这使得在Retrofit.create()
之后可以直接以RxJava
的风格做链式调用.
实现起来也非常简单, 只要.addConverterFactory()
.addCallAdapterFactory()
即可, 因为Retrofit
对外提供了抽象类Converter.Factory
CallAdapter.Factory
, 并且实例全部使用泛型来表示, 通过对外提供统一接口, 只要外部可以继承抽象工厂类并返回实现了统一接口的实例类, 就可以在不改变Retrofit
一行代码的情况下接入其他库, 或者原有逻辑代码变动不大的情况下接入Retrofit
.
是时候上一波图了:
上面这张图是我基于Stay的这篇文章改的, 荣耀归他, 另外由于图滞后于
Retrofit
版本, 所以如果有不一致的地方锅也归他(目前没看见不一致).
参考
这篇文章分析了Retrofit
的实现, 其优缺点即使不是一目了然, 也是足够清晰了, 希望能够帮助到正在选择网络请求框架的各位.
本文参考如下: