作者:@荒井。
所分析的源代码基于Retrofit 2.3.0版本。
一般我在刚接触一个库时,会先大致了解一下它解决什么问题,使用了什么技术等等,这之后我会动手写一个小例子并运行起来看看效果,那么首先我来写一个例子。
根据官网描述,使用Retrofit2时,是把HTTP请求写成Java接口形式,例如:
public interface ArticleService {
@GET("users/{user}/articles")
Call<ResponseBody> listArticles(@Path("user") String user);
}
然后,构建一个Retrofit对象,并使用它生成ArticleService接口的实现,例如:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("article.sample.com")
.build();
ArticleService service = retrofit.create(ArticleService.class);
接下来,使用生成的"service"对象,调用其中的方法得到一个Call<ResponseBody>类型对象"articles",代码如下:
Call<ResponseBody> articles = service.listArticles("arai");
最后我将这个请求加入队列,并等待返回结果回调:
articles.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
//成功。
}
@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
//失败。
}
});
如果请求发送成功,即可在onResponse回调方法中拿到返回的数据,如果失败则会回调onFailure方法。这样就是一个比较完整和基本的Retrofit2使用例子了。为了更好地熟悉和使用Retrofit2这个库,我准备稍微深入地分析其源代码,那么这篇文章打算从Retrofit.create(Class<T> service)方法开始,一步一步揭开其神秘的面纱。由于这个方法的篇幅不算长,先大致看一下它的全貌:
public <T> T create(final Class<T> service) {
/* 代码段 1 */
Utils.validateServiceInterface(service);
/* 代码段 2 */
if (validateEagerly) {
eagerlyValidateMethods(service);
}
/* 代码段 3 */
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);
}
});
}
我把整个方法分为3个代码段,分为用代码段1、2、3注释。
代码段1非常简单,看名字就知道,验证service是否是一个interface,除此之外,如果点进方法查看,会发现它还限制了service不可再继承其它interface,否则也会抛出异常。
代码段2,根据validateEagerly的值决定是否进入if,这个validateEagerly是Retrofit类的一个布尔常量,还记得如何得到Retrofit类的实例吗,没错,用的是Retrofit的内部类Builder的build()方法来构造的,此处不贴代码,直接给出结论,validateEagerly的值为默认值false,这里不会进入if条件内执行。
那么怎样让其执行if内代码,它又做了什么呢?为了一探究竟,我在构建Retrofit对象时设validateEagerly的值为true:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("article.sample.com")
.validateEagerly(true)
.build();
然后看看这个if括号里面的eagerlyValidateMethods(Class<?> service)方法:
private void eagerlyValidateMethods(Class<?> service) {
Platform platform = Platform.get();
for (Method method : service.getDeclaredMethods()) {
if (!platform.isDefaultMethod(method)) {
loadServiceMethod(method);
}
}
}
这个方法首先会获取当前的平台,Retrofit2中定义了2种特殊平台:Android和Java8,显然我所在的是Android平台。接下来遍历service中声明的方法,在这里发现Android.isDefaultMethod(Method method)是直接返回false,也就是会进入if块内,并对每一个method调用loadServiceMethod(Method method)方法。
代码段2说到这里,让我们先短暂地跳出来,回顾一下。使用Retrofit2时,开发者定义好代表网络请求的interface,然后使用Retrofit.create()方法来获取实现这个interface的对象,这里会遇到比如分析注解的操作,比如之前的例子中的listArticles方法就有注解@GET("users/{user}/articles"),这个过程是稍微慢一些的,Retrofit2为interface中的每一个method创建一个对应的ServiceMethod对象,保存这个过程中的一些信息,以便后面复用。那么根据validateEagerly这个名字,好像是要尽早执行这个操作的意思,此处暂时先记着有这样一回事,后面会搞明白的。
/* 代码段 3 */
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.
/* 代码段 3.1 */
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
/* 代码段 3.2 */
if (platform.isDefaultMethod(method)) {
return platform.invokeDefaultMethod(method, service, proxy, args);
}
/* 代码段 3.3 */
ServiceMethod<Object, Object> serviceMethod = (ServiceMethod<Object, Object>) loadServiceMethod(method);
/* 代码段 3.4 */
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
/* 代码段 3.5 */
return serviceMethod.callAdapter.adapt(okHttpCall);
}
});
接下来看代码段3,这里用到了Java的动态代理,直接返回动态代理对象,根据Retrofit.create(final Class<T> service)方法的定义,它返回的是一个实现了Service接口的对象。现在我们重点关注Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)方法的第三个参数,当我在实际调用返回对象中的方法时,会触发这个InvocationHandler对象"h"中的invoke(Object proxy, Method method, Object[] args)方法,在最开始的Retrofit使用例子中已经得知,调用返回的是Call<ResponseBody>类的对象,那么我分析一下调用过程,为了方便分析,我又把它分成若干个注释标记的代码段。
代码段3.1非常简单,判断方法存在于用户定义的interface中还是Object类中,定义在Object类中的方法是不需要代理的,所以这里判断false才会往下面执行,如果是true则直接调用。
代码段3.2的判断方法在之前的代码段2处已经分析过,判断条件会返回false,这里会直接跳过。那么如果没有跳过,会调用直接调用interface中的default方法,这是在Java8中才加入的特性,即假如我在之前的例子ArticleService中加入一个default方法的话,会直接传入参数并执行这个方法。
代码段3.3,在这里果然又遇到了loadServiceMethod(Method method)方法,这正是在分析代码段2时没有分析完的那个点。之前我说过,Retrofit2会为interface中的每一个method建立一个ServiceMethod<?, ?>对象,并存储起来。这个对象中包含了对method的分析结果,比如它的返回值、它含有的注解等。loadServiceMethod(Method method)方法的逻辑是先从Retrofit.serviceMethodCache中拿取这个方法所对应的ServiceMethod对象,如果拿到就直接返回,拿不到则构建这个对象,并存储到Retrofit.serviceMethodCache中。
ServiceMethod对象的构建过程还是稍微有些复杂的,它会分析method的全部注解、返回值的合法性等,并保证所定义的method能提供一个网络请求所必须的信息,同时还会获取这个method的Converter和CallAdapter,这两个概念在Retrofit2中很重要,在最开始的例子中,我所定义的interface中的method,返回的类型是Call<ResponseBody>,ResponseBody即代表HTTP请求所返回的Body,通过解析它得到我们要的数据。为了减轻开发者的负担,Retrofit2提供了CallAdapter和Converter这两个概念,可以将Call和ResponseBody转为其它类型,这让我们十分受益,例如使用Retrofit2+Rxjava开发时,我们可以将Service中的方法像下面那样定义,就可以只关注拿到数据后怎么展示,而不用去解析ResponseBody,并且可以直接写出Rxjava式的代码。
public interface ArticleService {
@GET("users/{user}/articles")
Observable<ArticleData> listArticles(@Path("user") String user);
}
解释完loadServiceMethod(Method method)方法都做了什么,回到分析代码段2时留下的问题。是否设置validateEagerly的区别,在于设置validateEagerly为true的话,ServiceMethod对象会在Retrofit.create()时就构建,而默认情况false,会在第一次调用方法时构建,至于哪个好,我猜了一下,可能未必对,我觉得还要视情况而定,如果一个interface中的method是在APP运行过程中必然要执行的方法,可能提早构建好一些,如果并不是APP运行期间必须要调用的方法,那可能默认false好一些。我又特意查找了网上对这一块有描述的一些文章,觉得有一种说法比较靠谱,即设validateEagerly为true会使method定义的正确性在调用Retrofit.create()就得到验证,这样方便开发时进行测试。
代码段3.4,通过上一步骤得到的serviceMethod对象和传入invoke()方法的参数,建立okHttpCall对象,OkHttpCall<T>类实现了Call<T>接口,这个类主要的作用就是得到它之后,便可以将其加入请求队列,发送网络请求。
代码段3.5,刚才代码段3.3分析中已经说过,CallAdapter的作用是将Call<T>转换为其它类型,那么这里由于我没有任何设置,源码会使用默认的CallAdapter,不进行任何转换,所得到的还是Call<T>类型,并直接返回。
那么通过Retrofit.create()方法得到一个实现interface的动态代理对象的大致过程到这里就写完了,休息一下。