Retrofit2源码学习笔记(一)

作者:@荒井。
所分析的源代码基于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类型对象"articles",代码如下:

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 service)方法开始,一步一步揭开其神秘的面纱。由于这个方法的篇幅不算长,先大致看一下它的全貌:

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 serviceMethod = (ServiceMethod) loadServiceMethod(method);
            OkHttpCall 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 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 service)方法的定义,它返回的是一个实现了Service接口的对象。现在我们重点关注Proxy.newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)方法的第三个参数,当我在实际调用返回对象中的方法时,会触发这个InvocationHandler对象"h"中的invoke(Object proxy, Method method, Object[] args)方法,在最开始的Retrofit使用例子中已经得知,调用返回的是Call类的对象,那么我分析一下调用过程,为了方便分析,我又把它分成若干个注释标记的代码段。

代码段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即代表HTTP请求所返回的Body,通过解析它得到我们要的数据。为了减轻开发者的负担,Retrofit2提供了CallAdapter和Converter这两个概念,可以将Call和ResponseBody转为其它类型,这让我们十分受益,例如使用Retrofit2+Rxjava开发时,我们可以将Service中的方法像下面那样定义,就可以只关注拿到数据后怎么展示,而不用去解析ResponseBody,并且可以直接写出Rxjava式的代码。

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类实现了Call接口,这个类主要的作用就是得到它之后,便可以将其加入请求队列,发送网络请求。

代码段3.5,刚才代码段3.3分析中已经说过,CallAdapter的作用是将Call转换为其它类型,那么这里由于我没有任何设置,源码会使用默认的CallAdapter,不进行任何转换,所得到的还是Call类型,并直接返回。

那么通过Retrofit.create()方法得到一个实现interface的动态代理对象的大致过程到这里就写完了,休息一下。

你可能感兴趣的:(Retrofit2源码学习笔记(一))