Retrofit是目前使用的最Six的网络请求框架,他具有很好的拓展性,支持Xml,Json,Protobuf等协议,调度上支持RxJava,Guava,Java8等方式。为了更好理解Retrofit,我看了下他的源码,发现Square就是牛,代码分层清晰,可复用性强,提供的API对开发者友好诸多优点,虽然用到了反射,但是依然不影响它的优美之处。

模仿Retrofit

对于Retrofit我们可以通过一个接口,生成一个对象,这个是如何做到的呢?一开始我就是带着这个问题来看源码的,为了更好理解我自己模拟了Retrofit的实现方式,我们也给这个关键类起名叫Retrofit,其使用方式如下。

code 1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Dynamicproxy {


public static interface VPN {
@Url("twitter.con")
public String visitNetwork();
}

public static interface ChinaNetwork {
@Url("baidu.com")
public String visitNetwork();

}


public static void main(String[] args) {

VPN vpn = new Retrofit().create(VPN.class);

System.out.println(vpn.visitNetwork());;
}
}

就是new Retrofit().create(VPN.class)让我满脸的疑惑,现在我们可以看下visitNetwork的结果,实际上是打印的twitter.con.若是我们把VPN换成ChinaNetwork,打印的是baidu.com.

这个API是不是和Retrofit有几分神似?

我们可以知道拿到Url注解里的value其实不难,可以参考之前注解的文章,主要拿到了这个方法,就可以获得和它相关的注解。

那么,怎么生产一个通过接口生成一个对象呢,其实很简单,就是动态代理?

动态代理

动态代理不是什么新词,在Java里很多框架都是基于动态代理实现的,动态代理在很大程度上解决了解耦的问题,不像静态代理(或者继承)那样强关联。
在了解动态代理前,我们需要先了解一个静态方法。
java.lang.reflect.Proxy的newProxyInstance方法,它需要三个参数:

  1. ClassLoader 这个是我们加载class中很必要的。
  2. Class[] 这个实际上就是我们需要使用动态代理的接口或者抽象类
  3. InvocationHandler 实际上就是这个对象在运行中的回调,并返回对应函数的结果。

可以结合code1的代码来实现我们的动态代理类:
code 2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class Retrofit {

public <T> T create(final Class<T> service) {
return (T) Proxy.newProxyInstance(service.getClassLoader(),
new Class[]{service}, new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before");
Annotation[] annotations = method.getAnnotations();
String html = "";
for (Annotation annotation : annotations) {
if (annotation instanceof Url) {
String url = ((Url) annotation).value();
System.out.println("访问的页面: " + url);
html = "<html > " + url + " </html>";
}


}

System.out.println("after");
return html;
}
});
}
}

总结

Retrofit2.0通过注解拿到我们配置的各种参数,如请求方法,url,请求参数等,再通过动态代理生成一个新的VPN或者ChinaNetwork对象来处理这些参数,像code2中我们可以通过拿到的url参数,来进行请求网页等其他操作,但对于程序的入口类是无感知的。