美文网首页
Retrofit2源码学习笔记(一)

Retrofit2源码学习笔记(一)

作者: 荒井 | 来源:发表于2017-06-28 00:32 被阅读213次

作者:@荒井
所分析的源代码基于Retrofit 2.3.0版本。

一般我在刚接触一个库时,会先大致了解一下它解决什么问题,使用了什么技术等等,这之后我会动手写一个小例子并运行起来看看效果,那么首先我来写一个例子。

根据官网描述,使用Retrofit2时,是把HTTP请求API写成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();

接下来看看这个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)方法。

使用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,这里用到了动态代理,直接返回动态代理对象,根据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即代表网络请求中所返回的信息,通过解析它得到返回的具体内容并执行业务逻辑操作。这样有时很不方便,Retrofit2提供Converter这个概念,可以将ResponseBody转为任意类型以供用户操作。又提供CallAdapter这个概念,可以将Call转为其它类型,例如使用Retrofit2+Rxjava开发的话,可以将Call转为Observable,以方便开发。

解释完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的动态代理对象的大致过程了。

那么Retrofit.create(Class<T> service)方法的分析就写完了,后面我还会再写Retrofit2其它部分的分析笔记,之前一直不怎么写文字,打算多写一写,后面自己可以回顾,顺带也提高一些写作能力。

相关文章

网友评论

      本文标题:Retrofit2源码学习笔记(一)

      本文链接:https://www.haomeiwen.com/subject/kkcczttx.html