Retrofit 原理 运行机制详解

阅读此篇博客,你需要备Java动态代理的知识、反射和注解相关的知识,不然你阅读可能有些困难

回忆Retrofit的使用场景

在使用Retrofit的时候,我们经常通常看到有一个用于请求的接口,然后使用

Api api= retrofit.create(Api.class);

这样子创建了以后,这个接口Api里面的方法就能正常使用了,博主之前也是很好奇,之前误以为是,这里创建的时候是帮我实现了接口中所有的方法,然而今天学习了动态代理之后才知道并非如此,如果大家没有动态代理的知识储备,建议先去了解一下,不然下面可能你看不大懂

简单总结Java动态代理

我稍微总结一下Java的动态代理:

这是一种设计模式,我个人理解,就是一个需要被代理的类A,如果A是一个接口,那么会有一个实现类B,创建代理对象C,这时候,你想调用A中的方法,就可以调用C中的方法,所有方法的调用都会经过InvocationHandler接口中的invoke方法

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

            // 返回方法的结果
            return null;
        }

从此方法中可以看出有
1.要执行的方法:method
2.方法要执行的参数:args
3.还有一个代理类对象:proxy(这个对象就是上述举例中的C对象)
4.还有一个返回值,表示调用此方法的返回值

所以有了这些参数,你就完全可以控制每一个方法的执行,和每一个方法返回的结果了,这里唯一缺少的就是此方法执行的对象,也就是缺少上述中例子中的A(如果A是正常的类)或者B(如果A是接口)

但是我们今天讨论的不是正常的一个Aop思想的代理。Aop就是在上述invoke拦截方法中调用真正的对象A的过程,调用之前和之后你都可以做一定的操作,这里不再细说

今天要讨论的是A类是一个接口,我们并没有写实现类,为什么Retrofit框架能执行里面的方法并给我们想要的请求结果呢

Retrofit真正实现机制

比如这个名称为Api的接口,里面有一个方法

/**
 * 接口
 * 
 * @author cxj
 *
 */
public interface Api {

    /**
     * 
     * @param name
     *            用户名
     */
    @Post("/postTest")
    public String postTest(String name);

}

然后你在此方法上描述了你的请求,比如是Post的请求,这里是为了让大家回忆起来
@Post注解也是博主造假的

@Target({ ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface Post {

    // 请求地址
    String value();

}

然后我们有这么一步

// 创建接口实现类,代理对象搞定
Api api = Retrofit.create(Api.class);

然后我们就可以调用api中的方法啦,我们上述讲到,如果使用代理来实现,此处的Api接口就是上述例子中的A(是一个接口)

然后返回的代理对象由于是Api的实现类,所以直接可以赋值给Api(多态)
如果这时候我们调用api中的方法
全部代码:

    public static void main(String[] args) {

        // 创建接口实现类,代理对象搞定
        Api api = Retrofit.create(Api.class);

        System.out.println(api.getClass().getName());

        String result = api.postTest("cxj");

        System.out.println("result = " + result);

    }

我们上述说了,代理类的所有方法都会执行代理拦截类中的invoke方法

这是我造假的Retrofit对象

public class Retrofit {

    /**
     * 创建代理对象
     * 
     * @param service
     * @return
     */
    @SuppressWarnings("unchecked")
    public static  T create(Class service) {
        return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class[] { service },
                new MInvocationHandler());
    }

    static class MInvocationHandler implements InvocationHandler {

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

            // 返回方法的结果
            return "hello";
        }

    }

}

Retrofit类中创建代理对象的方法可以去查阅其他资料或者博文,这里不讲。
创建的时候我们可以看到,第三个参数是new MInvocationHandler();
即下面这个静态的内部类,就是代理类拦截接口的实现类,所以这里面的invoke方法在调用

String result = api.postTest("cxj");

这句代码的时候会被执行,而我呢啥事情也没干,直接返回了一个hello

看运行结果

Retrofit 原理 运行机制详解_第1张图片

你会发现方法返回了hello

那我们在Api中添加一个方法呢

Retrofit 原理 运行机制详解_第2张图片

然后我们在main方法中调用此方法

Retrofit 原理 运行机制详解_第3张图片

你会发现同样是返回了hello.

小总结

所以你要彻底明白了一个事情,那就是网络框架Retrofit在实现你的接口中的方法的时候,并没有真正的去调用某一个对象的某一个方法.而是在invoke方法中读取了你调用的方法的
1请求地址
2.请求方式
3请求参数
……..

然后返回你一个Call对象,Call里面调用enqueue方法才是真正的发出了网络请求,这个网络请求的各个信息就是在invoke方法中读取然后封装的

再加工一下我们的代码,假如我想读取我调用方法是什么方法,并且访问的路径是什么,那我们是不是也应该在invoke方法中做手脚呢?答案是明显的

改造后的invoke方法

        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

            // 判断方法上是否有Post注解
            boolean b = method.isAnnotationPresent(Post.class);
            if (b) {// 如果有
                // 拿到注解
                Post post = method.getAnnotation(Post.class);
                // 拿到访问的路径
                String url = post.value();
                System.out.println("是一个Post请求,并且返回的路径是:" + url + "\n");
            }

            // 判断方法上是否有Get注解
            b = method.isAnnotationPresent(Get.class);
            if (b) {// 如果有
                // 拿到注解
                Get get = method.getAnnotation(Get.class);
                // 拿到访问的路径
                String url = get.value();
                System.out.println("是一个Get请求,并且返回的路径是:" + url + "\n");
            }

            // 返回方法的结果
            return "hello";
        }

需要你有反射和注解之类的知识!

你可以看到我获取了方法上的注解,然后获取了注解里面的属性,这不就是Retrofit框架做的事情么,拿到了你方法上的各个注解和参数中的注解,然后进一步拿到数据

运行结果:

Retrofit 原理 运行机制详解_第4张图片

Retrofit 原理 运行机制详解_第5张图片

感谢

本篇博客只是点了一下Retrofit的代理实现,没有那么详细的去讲更多的细节,但是已经点出了最核心的一点,也是博主之前最有疑问的一个地方.

你可能感兴趣的:(Android,Java)