作者:@荒井。
所分析的源代码基于Retrofit 2.3.0版本。
一般我在刚接触一个库时,会先大致了解一下它解决什么问题,使用了什么技术等等,这之后我会动手写一个小例子并运行起来看看效果,那么首先我来写一个例子。
根据官网描述,使用Retrofit2时,是把HTTP请求写成Java接口形式,例如:
public interface ArticleService {
@GET("users/{user}/articles")
Call 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
Call articles = service.listArticles("arai");
最后我将这个请求加入队列,并等待返回结果回调:
articles.enqueue(new Callback() {
@Override
public void onResponse(Call call, Response response) {
//成功。
}
@Override
public void onFailure(Call call, Throwable t) {
//失败。
}
});
如果请求发送成功,即可在onResponse回调方法中拿到返回的数据,如果失败则会回调onFailure方法。这样就是一个比较完整和基本的Retrofit2使用例子了。为了更好地熟悉和使用Retrofit2这个库,我准备稍微深入地分析其源代码,那么这篇文章打算从Retrofit.create(Class
public T create(final Class 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
我把整个方法分为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 serviceMethod = (ServiceMethod) loadServiceMethod(method);
/* 代码段 3.4 */
OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
/* 代码段 3.5 */
return serviceMethod.callAdapter.adapt(okHttpCall);
}
});
接下来看代码段3,这里用到了Java的动态代理,直接返回动态代理对象,根据Retrofit.create(final Class
代码段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
public interface ArticleService {
@GET("users/{user}/articles")
Observable 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
代码段3.5,刚才代码段3.3分析中已经说过,CallAdapter的作用是将Call
那么通过Retrofit.create()方法得到一个实现interface的动态代理对象的大致过程到这里就写完了,休息一下。