一、引言
Retrofit
和 OKHttp
同为 square
出品的网络请求相关库,不同的是 Retrofit
本身不进行网络请求,而是作为一个协调者,协调其他组件共同处理网络请求。用官网描述来说就是:Retrofit
是可插拔的,它允许不同的执行机制和类库用于执行HTTP
请求、允许不同序列化的类库进行java
实体类与HTTP
响应数据之间转换。
Retrofit
的网络请求部分默认基于OkHttp
,关于OkHttp
,鄙人写过 OkHttp源码分析
一文,感兴趣的童鞋可以看看。
本文纯属基于个人理解,源码解析不限于执行流程,因此受限于知识水平,有些地方可能依然没有理解到位,还请发现问题的童鞋理性指出。
温馨提示:本文源码基于
Retrofit-2.4.0
二、流程分析
1. 简单使用
这里以请求 玩Android 首页数据为例,演示使用Retrofit
进行网络请求的最基本方式。
首先如下初始化 Retrofit
:
public void initializeRetrofit() {
retrofit = new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.baseUrl("http://wanandroid.com")
.build();
}
然后如下建立请求接口:
public interface Service{
@GET("article/list/{page}/json")
public Call> getHomeList(@Path("page")int page);
}
接着如下调用请求、处理响应数据:
public void getHomeList(int page, ResCallback> callback){
if (service == null)
service = retrofit.create(Service.class);
service.getHomeList(page).enqueue(new Callback>() {
@Override
public void onResponse(Call> call, Response> response) {
System.out.println(response.message());
System.out.println(response.code());
System.out.println(response.headers());
if (response.isSuccessful()){
ResponseEntry body = response.body();
if (body == null) {
callback.onFailed(new Exception("body is null !!"));
return;
}
callback.onSuccess(body);
}
}
@Override
public void onFailure(Call> call, Throwable t) {
}
});
}
上面可以注意到的一点是,不同于直接使用OkHttp
,这里response.body()
可以直接拿到我们需要的解析好的Java
实体类了,而不需要再做Json
数据解析工作, 它的使用过程如下:
而一般来说,我们使用OkHttp
进行网络请求的使用过程如下:
显然 Retrofit
的目的就是把网络请求、响应数据解析等相互分离的操作都整合到一起,达到 All in one
的效果,而实际请求和解析都是以可插拔的插件形式存在,灵活度非常高。
2. 创建服务到建立Call
过程分析
关于 Retrofit
的构建 ,我们注意一下必填参数以及默认参数即可,根据如下Retrofit.Build#build
源码可知:
-
baseUrl
必填 - 默认
callFactory
为OkHttpClient
;默认callbackExecutor
(回调执行器)在Android
中是主线程的Handler
;默认会先添加Retrofit
内部的转换器,然后是其他,比如我们自定义的转换器,这是为了避免内部转换器的行为被复写掉,以及确保使用消耗(consume
)所有类型的转换器时能有正确的行为。
public Retrofit build() {
// baseUrl必填
if (baseUrl == null) {
throw new IllegalStateException("Base URL required.");
}
// 默认Call工厂为 OkHttpClient
okhttp3.Call.Factory callFactory = this.callFactory;
if (callFactory == null) {
callFactory = new OkHttpClient();
}
// 默认回调执行器为主线程Handler
Executor callbackExecutor = this.callbackExecutor;
if (callbackExecutor == null) {
callbackExecutor = platform.defaultCallbackExecutor();
}
List callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor));
List converterFactories = new ArrayList<>(
1 + this.converterFactories.size() + platform.defaultConverterFactoriesSize());
// 这里会先添加Retrofit内部的转换器再添加我们自定的转换器
converterFactories.add(new BuiltInConverters());
converterFactories.addAll(this.converterFactories);
converterFactories.addAll(platform.defaultConverterFactories());
// ...
}
这里关注一下Android
平台的回调执行器,因为回调执行在主线程的Handler
上,因此可以在回调中直接操作UI
控件。
static class Android extends Platform {
@Override public Executor defaultCallbackExecutor() {
return new MainThreadExecutor();
}
static class MainThreadExecutor implements Executor {
// UI线程
private final Handler handler = new Handler(Looper.getMainLooper());
@Override public void execute(Runnable r) {
handler.post(r);
}
}
// ...
}
接着来分析一下使用Retrofit#create
创建一个请求服务实例时发生了什么,Retrofit#create
源码如下,可知:
首先需要确定的是
service
本身是个接口,并且不继承于其他接口。-
然后重点来了,
eagerlyValidateMethods
会通过反射获取service
接口中所有的方法,接着尝试从ServiceMethod
缓存池中查找对应于各个方法的ServiceMethod
,如果没找到的话,则重新通过ServiceMethod.parseAnnotations
去解析各个方法的注解,解析完成后将返回的ServiceMethod
(这里返回的ServiceMethod
其实是实现类HttpServiceMethod
,HttpServiceMethod
会负责根据解析的注解参数创建Call
,并在HttpServiceMethod#invoke
调用时执行网络请求)存入缓存池中,方便后续复用,这里缓存池的作用跟线程池的概念异曲同工,都是为了减少因为每次都解析(创建)而造成的不必要的性能损耗,所以干脆花点内存存起来省事儿。eagerlyValidateMethods
执行过程如下: -
接着通过
Proxy.newProxyInstance
给服务接口创建一个代理实例,实际可转成对应接口的类型,这里主要关注一下InvocationHandler
, 每个Proxy
对象实例都会绑定一个InvocationHandler
对象,当执行Proxy#invok
方法时,最终对派发给InvocationHandler#invok
,也就是说,我们通过服务接口实例调用接口方法时,最终都会通过InvocationHandler#invok
去执行。invoke
方法执行链如下:
public T create(final Class 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();
private final Object[] emptyArgs = new Object[0];
@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);
}
// 这里实际最终执行的是 HttpServiceMethod#invoke(..)
return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
}
});
}
private void eagerlyValidateMethods(Class> service) {
Platform platform = Platform.get();
for (Method method : service.getDeclaredMethods()) {
// 在 Android 7.0 以前版本都是 false,Android 7.0 及以上则根据 `isDefaultMethod`的复写值决定
if (!platform.isDefaultMethod(method)) {
loadServiceMethod(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 = ServiceMethod.parseAnnotations(this, method);
// 然后将解析结果添加到缓存,以便后续复用
serviceMethodCache.put(method, result);
}
}
return result;
}
上面的 parseAnnotations
执行链如下:
我们顺着这条链看看,首先是ServiceMethod#parseAnnotations
:
abstract class ServiceMethod {
static ServiceMethod parseAnnotations(Retrofit retrofit, Method method) {
// 1. 解析方法的注解参数,保存在 RequestFactory
RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);
// ...
// 2. 使用将上面解析的参数建立Call,用于网络请求
return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
}
}
接着是RequestFactory#parseAnnotations
,源码如下,主要做了三件事情,看注释即可:
final class RequestFactory {
static RequestFactory parseAnnotations(Retrofit retrofit, Method method) {
return new Builder(retrofit, method).build();
}
static final class Builder{
RequestFactory build() {
// 1. 解析每个方法的注解
for (Annotation annotation : methodAnnotations) {
parseMethodAnnotation(annotation);
}
// ...
int parameterCount = parameterAnnotationsArray.length;
parameterHandlers = new ParameterHandler>[parameterCount];
// 2. 解析方法参数
for (int p = 0; p < parameterCount; p++) {
parameterHandlers[p] = parseParameter(p, parameterTypes[p], parameterAnnotationsArray[p]);
}
// ...
// 3. 创建 RequestFactory 保存参数
return new RequestFactory(this);
}
}
}
接着是 HttpServiceMethod#parseAnnotations
,源码如下:
static HttpServiceMethod parseAnnotations(
Retrofit retrofit, Method method, RequestFactory requestFactory) {
// 1. 获取 Call 适配器
CallAdapter callAdapter = createCallAdapter(retrofit, method);
Type responseType = callAdapter.responseType();
// 2. 获取响应数据转换器
Converter responseConverter = createResponseConverter(retrofit, method, responseType);
okhttp3.Call.Factory callFactory = retrofit.callFactory;
// 3. 根据解析的参数创建 HttpServiceMethod
return new HttpServiceMethod<>(requestFactory, callFactory, callAdapter, responseConverter);
}
HttpServiceMethod#invok
执行时源码如下:
@Override ReturnT invoke(Object[] args) {
// 创建一个 OkHttpCall, 用于进行网络请求和响应数据转换
return callAdapter.adapt(
new OkHttpCall<>(requestFactory, args, callFactory, responseConverter));
}
至此,便是一个服务接口从解析到创建成一个OkHttp#Call
的过程,纵观全局,其实这个过程就好比一个为了将如下接口:
public interface Service{
@GET("article/list/{page}/json")
public Call> getHomeList(@Path("page")int page);
}
解析成一个请求链接为http://wanandroid.com/article/list/0/json
,请求方式为 GET
,请求的调用方式为:
Service service = ...;
// 相当于执行 HttpServiceMethod#invoke 方法
Call> = service.getHomeList(0);
的过程,而这个过程中需要解决将接口转换成对象实例、将方法注解、参数解析处理拼接为请求连接、最后确定返回类型的问题,此时Call
尚未进行请求;
3. Call
请求执行到响应数据回调过程分析
关于OkHttp#Call
如何运作的问题已经在 OkHttp源码解析 一文中做了详细分析,这里的不同之处在于,在Retrofit
中我们需要更多地关注它是如何协调请求和响应,最终回调给UI
线程的。
OK,从HttpServiceMethod#invoke
出发,根据前面的内容中我们已经知道它会通过callAdapter.adapt(new OkHttpCall<>(requestFactory, args, callFactory, responseConverter))
返回一个Call
实例,并且在Android
平台上会将响应数据回调在UI
线程的Handler
上,因此我们先关注一下Android
平台下的默认CallAdapter
,于是定位到Android#defaultCallAdapterFactories
:
@Override List extends CallAdapter.Factory> defaultCallAdapterFactories(
@Nullable Executor callbackExecutor) {
if (callbackExecutor == null) throw new AssertionError();
return singletonList(new ExecutorCallAdapterFactory(callbackExecutor));
}
可见Android
平台下的默认CallAdapter
是ExecutorCallAdapterFactory
, 于是可以定位到ExecutorCallAdapterFactory#adapt
:
@Override public Call
ExecutorCallbackCall
这里实际是使用了装饰器模式,它将工作委托给了callbackExecutor
和delegate
,而它自身仅仅起到了协调作用,将响应数据回调到UI
线程:
static final class ExecutorCallbackCall implements Call {
final Executor callbackExecutor;
final Call delegate;
ExecutorCallbackCall(Executor callbackExecutor, Call delegate) {
this.callbackExecutor = callbackExecutor;
this.delegate = delegate;
}
@Override public void enqueue(final Callback callback) {
checkNotNull(callback, "callback == null");
delegate.enqueue(new Callback() {
@Override public void onResponse(Call call, final Response response) {
// 回调至主线程
callbackExecutor.execute(new Runnable() {
@Override public void run() {
if (delegate.isCanceled()) {
callback.onFailure(ExecutorCallbackCall.this, new IOException("Canceled"));
} else {
callback.onResponse(ExecutorCallbackCall.this, response);
}
}
});
} // ...
});
} // ...
}
具体到网络请求的执行与响应数据的转换工作还得看OkHttpCall
,这里我们只关注一下OKHttpCall#enqueue
即可, 可见这里除了请求网络数据外,还会先转换响应数据后再回调给上一级:
@Override public void enqueue(final Callback callback) {
okhttp3.Call call;
Throwable failure;
// 1. 执行请求
call.enqueue(new okhttp3.Callback() {
@Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
Response response;
try {
// 2. 解析响应数据,将网络响应数据转换成指定数据类型
response = parseResponse(rawResponse);
} catch (Throwable e) {
// ...
return;
}
try {
// 3. 将解析完成的数据回调给上一级
callback.onResponse(OkHttpCall.this, response);
} catch (Throwable t) {
// ...
}
}
// ...
});
}
然后parseResponse
部分源码如下,可见这里会通过Converter
网络响应数据转换为我们指定的数据类型:
Response 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();
// ...
ExceptionCatchingResponseBody catchingBody = new ExceptionCatchingResponseBody(rawBody);
try {
// 通过转换器转换数据
T body = responseConverter.convert(catchingBody);
return Response.success(body, rawResponse);
} catch (RuntimeException e) {
// ...
}
}
综上可知,最终网络请求会在OkHttpCall
中执行,获取响应数据后通过设定的Converter
转换器将数据转换成指定类型;而最终回调给UI
线程则是在ExecutorCallbackCall
中进行,作为装饰器,它实际将请求和响应数据处理工作都委托给了OkHttpCall
,而自身仅仅做了最终数据的回调处理。
于是整体执行流程如下:
三、Proxy
这里指的是反射工具类中的java.lang.reflect.Proxy
,通过前面的分析,我们已经知道,我们建立的服务接口会通过Proxy.newProxyInstance
来实例化一个代理对象实例,而通过这个实例化的对象,就能像使用普通类对象实例一个调用方法。
这里我比较好奇的是它是如何给接口实例化的,因此咱就来研究研究,定位到Proxy#newProxyInstance
,精简一下源码(去除了验证逻辑等),如下,可以发现Proxy
会为我们的服务接口构建一个代理类(当然会先从代理类缓存,也就是WeakCache
中查找已经构建的代理类),然后通过这个类的构造函数构建出一个实例对象出来:
public static Object newProxyInstance(ClassLoader loader,
Class>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
final Class>[] intfs = interfaces.clone();
// 1. 从 `WeakCache`中查找,或者创建一个接口的代理类
Class> cl = getProxyClass0(loader, intfs);
// 2. 拿到代理类的构造函数
final Constructor> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
// ...
// 3. 通过构造函数创建一个实例对象
return cons.newInstance(new Object[]{h});
}
再来看看getProxyClass0()
, 根据代码注释可知,如果根据类加载器查找已经实现的代理类,那么直接返回拷贝的缓存,如果没找到,那么就会通过ProxyClassFactory
去创建一个代理类。
private static Class> getProxyClass0(ClassLoader loader,
Class>... interfaces) {
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
return proxyClassCache.get(loader, interfaces);
}
于是再来看看ProxyClassFactory
,可知通过其apply
方法会根据我们服务接口的信息配置代理类,然后通过ProxyGenerator
生成一个代理类class
文件,最终通过defineClass0
将这个代理类定义出来:
private static final class ProxyClassFactory
implements BiFunction[], Class>>
{
private static final String proxyClassNamePrefix = "$Proxy";
@Override
public Class> apply(ClassLoader loader, Class>[] interfaces) {
Map, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
// ...
int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
// 1. 进行一系列的代理类信息的配置
//...
// 2. 根据配置信息生成代理类class文件
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);
// 3. 最终生成特定代理类
return defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length);
}
}
这里的defineClass0
是个native
方法,因此就不再深挖了:
private static native Class> defineClass0(ClassLoader loader, String name, byte[] b, int off, int len);
至此,是不是已经明白了Proxy
如何实例化接口的呢?
四、总结
通过上面的分析,可以发现 Retrofit
更像是对一个OkHttp
请求的抽取与封装:
- 网络请求参数全部抽离成服务接口方法的注解,注解参数解析和
Request
构建工作抽离到了RequestFactory
。 -
CallAdapter
将OkHttpCall
的执行匹配到我们指定的执行器,而Converter
则将网络响应数据转换成我们想要的类型 - 最终,在
Android
平台上直接将指定的数据类型返回给UI
线程的Handler
处理。
关于Proxy
,它将服务接口转换成一个代理类对象实例的实现方式也很值得我们学习。