本篇文章的目的是介绍 注解 反射 以及 动态代理在 Retrofit 框架中的体现。
首先带着几个问题去开始以下的文章:
- 注解在 Retrofit 中是如何体现的,有什么作用的?
- 反射在 Retrofit 中是如何体现的,有什么作用的?
- 动态代理在 Retrofit 中是如何体现的,有什么作用的?
在回答以上几个问题之前,先让本少爷来给你解释一下几个概念:
注解: 可以认为是一个标签,只是这个标签附带有数据,比如给张 三贴上一个 渣男 的标签,那这个 渣男 的标签含有的 数据 就是-张三玩弄感情的证据。
反射:那既然通过注解保存了数据信息,当然要有方法去获取到注解中数据,这个方法就是反射,通过反射去获取注解上的信息。比如有了张三现在有了渣男的标签,现在就可以通过反射去拿到张三玩弄感情的证据
代理模式:指的是通过代理对象持有对真实对象的引用,通过代理对象实现对真实对象的操作。比如张三通过 某APP 约小姐姐一起去运动
代理模式又分为静态代理与动态代理,想了解详细内容可查看本少爷代理模式的专题。
通过手写一个简易版的 Retrofit 的应用来解释他们在Retrofit中的应用:
- 首先要定义注解—— 规定请求的方式,参数的格式等
//自定义注解请求的方式
@Target(METHOD)
@Retention(RUNTIME)
public @interface POST {
String value() default "";
}
---------
//自定义注解-请求的参数的格式
@Target(PARAMETER)
@Retention(RUNTIME)
public @interface Field {
String value();
}
- 定义网络接口,把刚才定义好的注解添加到接口的方法上,然后通过这个接口来请求数据。
了解过Retrofit的都知道Retrofit实际上是通过okhttp去请求数据的,所以这里使用了okhttp的Call。
public interface API {
@POST("/v3/weather/weatherInfo")
Call postWeather(@Field("city")String city,@Field("key")String key);
@GET("/v3/weather/weatherInfo")
Call getWeather(@Query("city") String city, @Query("key")String key);
}
- 定义一个Retrofit 类,里面含有两个最重要的参数
1)请求的地址(baseUrl)
2)实现请求的引擎(okHttpCall)
public EasRetrofit(Call.Factory factory, HttpUrl baseUrl) {
this.factory = factory;
this.baseUrl = baseUrl;
}
通过建造者模式来实现对Retrofit这个类的封装:
public static final class Builder {
private HttpUrl baseUrl;
private okhttp3.Call.Factory factory;
public Builder baseUrl(String baseUrl) {
this.baseUrl = HttpUrl.get(baseUrl);
return this;
}
public Builder callFactory(okhttp3.Call.Factory factory) {
this.factory = factory;
return this;
}
public EasRetrofit build() {
if (baseUrl == null) {
throw new IllegalStateException("Base URL required.");
}
factory = this.factory;
if (factory == null) {
factory = new OkHttpClient();
}
return new EasRetrofit(factory, baseUrl);
}
}
- 通过动态代理生成第2步定义的网络接口对象
public <T> T create(Class<T> service) {
return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class[]{service},
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//解析这个method 上所有的注解信息
ServiceMethod serviceMethod = loadServiceMethod(method);
//args
return serviceMethod.invoke(args);
}
});
}
其中InvocationHandler可以理解是一个监听,当调用到接口对象中的方法时,这个监听就会触发,就会拿到接口对象中对应的方法,对应的参数信息。
-
通过这个监听就可以实现对接口对应方法中的处理:
1)处理方法上的注解,获取注解包含的数据信息
2)封装数据信息,提供多次复用
这个封装类就是 ServiceMethod,同样也是通过对建造者的模式进行封装。 -
在网络请求中肯定存在多次请求相同的情况,因此对请求信息进行一个简单的封装:
//通过 ConcurrentHashMap 来存储请求信息
private Map<Method, ServiceMethod> serviceMethodCache = new ConcurrentHashMap<>();
-------
private ServiceMethod loadServiceMethod(Method method) {
//先不上锁,避免synchronized的性能损失
ServiceMethod result = serviceMethodCache.get(method);
if (result != null) {
return result;
}
//多线程下,避免重复解析
synchronized (serviceMethodCache) {
if (null == result) {
result = new ServiceMethod.Builder(this, method).build();
serviceMethodCache.put(method, result);
}
}
return result;
}
- 返回一个okhttpCall
Request request = new Request.Builder().url(url).method(httpMethod, formBody).build();
//okhttpCall
return factory.newCall(request);
让我们来看看是如何使用的:
EasRetrofit easRetrofit = new EasRetrofit.Builder().baseUrl("https://restapi.amap.com").build();
//可以理解是一个是一个监听,当监听到调用了接口中的方法时,就执行生成okhttpcall
api = easRetrofit.create(API.class);
-------------------------------------------------
//获取到监听中生成的okhttpcall对象
okhttp3.Call call = api.postWeather("110101", "ae6c53e2186f33bbf240a12d80672d1b");
//通过call进行异步请求
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
}
});
回到一开始的三个问题,我们来简单回答一下:
-
注解在 Retrofit 中是如何体现的,有什么作用的?
体现在网络接口的方法上,规定请求的格式,请求参数等。 -
反射在 Retrofit 中是如何体现的,有什么作用的?
体现在解析网络接口方法上注解中包含的数据,获取请求的数据。 -
动态代理在 Retrofit 中是如何体现的,有什么作用的?
体现在生成网络接口对象,封装网络请求的数据,实现一个类似于监听的功能。


网友评论