前段时间因为用到了Dubbo的接口,所以学习了一下Zookeeper+Dubbo的RPC调用方式,总结了一篇Blog【SpringBoot学习笔记 十四】SpringBoot+Dubbo+Zookeeper集成开发,最近呢因为有一个合同相关的开发任务,大量使用了Retrofit2,用之前也是半生不熟的仿写,所以还是希望进行一个系统性的学习整体了解下Retrofit2的用法和基本原理,因为Retrofit2是基于OkHttp进行封装的,所以近期会再对OkHttp进行一个系统性学习介绍,还是按照这样的脉络来梳理:
- 是什么:Retrofit2是什么,基本概念是什么
- 为什么:为什么选择用Retrofit2,在什么场景下用Retrofit2比较好
- 怎么用:基本的使用方法,怎么使用
- 底层原理:是什么样的底层原理和源码支撑Retrofit2的功能
按照这样的线路来进行一个深度理解。
Retrofit2基本概念
第一个问题,是什么?Retrofit2是现在比较流行的网络请求框架,可以理解为OkHttp的加强版,底层封装了OkHttp。准确来说,Retrofit2是一个Restful的Http网络请求框架的封装。因为网络请求工作本质上是由okhttp来完成,而Retrofit负责网络请求接口的封装,整体流程如下图所示:
本质上看,应用通过Retrofit请求网络,实质上是使用Retrofit接口层封装请求参数、Header、Url等信息,之后由OkHttp来完成后续的请求工作。在服务端返回数据后,OkHttp将原始数据交给Retrofit,Retrofit根据用户需求解析。
Retrofit2框架优势
第二个问题,为什么?既然已经有了OkHttp,为什么还要在上边封装一层Retrofit,那就回到了框架的本质,框架的作用是什么?让使用者少干活,只关注核心业务逻辑,那么Retrofit的作用就显而易见了,使用方便简介,让使用者傻瓜式进行web调用,除此之外还有如下几个点:
- 超级解耦 ,接口定义、接口参数、接口回调不再耦合在一起,Retrofit统统帮封装好了
- 可以配置不同的httpClient来实现网络请求,如OkHttp、HttpClient
- 支持同步、异步、Rxjava,最佳实践:Retrofit + OkHttp + RxJava
- 可以配置不同反序列化工具类来解析不同的数据,如json、xml
- 请求速度快,使用方便灵活简洁
光看这些优点可能还不是很懂,那么从OkHttp和Retrofit的使用方式对比一下吧:
OkHttp框架使用方式
样板步骤代码如下:
private void testOkHttp() throws IOException {
//Step1:创建HttpClient对象,也就是构建一个网络类型的实例,一般会将所有的网络请求使用同一个单例对象
final OkHttpClient client = new OkHttpClient();
//Step2:构建Request,也就是构建一个具体的网络请求对象,具体的请求url,请求头,请求体
RequestBody formBody = new FormBody.Builder()
.add("size", "10")
.build();
final Request request = new Request.Builder()
.url("https://www.baidu.com")
.post(formBody)
.build();
//Step3:构建请求Call,也就是将具体的网络请求与执行请求的实体进行绑定,形成一个具体的正式的可执行实体
Call call = client.newCall(request);
//step4 发送网络请求,获取数据,进行后续处理
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
Log.i(TAG,response.toString());
Log.i(TAG,response.body().string());
}
});
}
OkHttp 是基于Http协议封装的一套请求客户端,虽然它也可以开线程,但根本上它更偏向真正的请求,跟HttpClient
的职责是一样的。OkHttp主要负责socket部分的优化,比如多路复用,buffer缓存,数据压缩,但还有问题没有解决:
- 用户网络请求的接口配置繁琐,尤其是需要配置请求body,请求头,参数时
- 数据解析过程需要用户手动拿到responsbody进行解析,不能复用;
- 无法适配自动进行线程的切换
再来看看Retrofit的实现:
Retrofit框架使用方式
样板步骤代码如下:
//step1:创建retrofit对象, 构建一个网络请求的载体对象,和okhttp构建OkhttpClient对象一样,不过retrofit在build的时候有非常多的初始化内容,这些内容可以为后面网络请求提供准备,如准备 现成转换Executor,Gson convert,RxJavaCallAdapter
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://www.baidu.com/")
.addConverterFactory(GsonConverterFactory.create(new Gson()))
.build();
//step2:Retrofit的精髓,为统一配置网络请求完成动态代理的设置,参数等都在接口中通过注解体现
ISharedListService sharedListService = retrofit.create(ISharedListService.class);
//step3:构建具体网络请求对象Request
//1)将接口中的注解翻译成对应的参数;
//2)确定网络请求接口的返回值response类型以及对应的转换器;
//3)将Okhttp的Request封装成为Retrofit的OKhttpCall
Call<SharedListBean> sharedListCall = sharedListService.getSharedList();
//step4:进行网络请求,处理网络请求的数据
sharedListCall.enqueue(new Callback<SharedListBean>() {
@Override
public void onResponse(Call<SharedListBean> call, Response<SharedListBean> response{
if (response.isSuccessful()) {
System.out.println(response.body().toString());
}
}
@Override
public void onFailure(Call<SharedListBean> call, Throwable t) {
t.printStackTrace();
}
});
整体流程中二者对比如下:
Retrofit2使用实践
第三个问题,怎么用?大致明白为什么我们要使用Retrofit2框架后,我们需要在实践中体会它的好处。
Retrofit2常用注解
Retrofit使用大量注解来简化请求,Retrofit将okhttp请求抽象成java接口,使用注解来配置和描述网络请求参数。接下来可以依照这个示例来进行分析:
/**
* @Description: 盖章接口
* @Param: [headers, application, userId, contractSealManualParameter]
* @Author: tianmaolin
* @Date: 2021/12/17
*/
@POST("/tml/contract/contractSealManual")
Call<CommonContractResponse<String>> contractSealManual(@HeaderMap Map<String, String> headers,
@Query("application") String application, @Query("userId") Long userId,
@Body ContractSealManualParameter contractSealManualParameter);
大概可以分为以下几类:
1 请求方法注解
加在请求的方法上的注解,例如上例中我们看到的@POST("/tml/contract/contractSealManual")
,其它用的还有:
2 请求头注解
请求中header中放置的内容,例如上例中的:@HeaderMap Map<String, String> headers
,其它用的还有:
3 请求参数注解
请求在参数中防止的参数,例如上例中的:@Query("userId") Long userId
或者@Body ContractSealManualParameter contractSealManualParameter
,其它用的还有:
注意:接口中的每个方法的参数都要用注解标记,否则会报错。
4 请求和响应格式(标记)注解
这些注解一般是处理有特殊数据传输的请求:
Retrofit2使用步骤
通过以上的了解,不难发现Retrofit将okhttp请求抽象成java接口,采用注解描述和配置网络请求参数,用动态代理将该接口的注解“翻译”成一个Http请求,最后执行Http请求。大致了解了常用注解后,我们来按照上面样板代码中的步骤来进行框架使用。
1 定义通用的Retrofit处理方式
可以让所有的Retrofit基础这个基础的代理实现,将一些请求的共性提炼出来,例如设置请求的读超时时间,写超时时间,添加响应数据Convert等。
public class BaseRetrofitProxy<T> {
@Resource
private OkHttpClient okHttpClient;
@PostConstruct
public void initClient() {
okHttpClient = new OkHttpClient.Builder().connectTimeout(TIMEOUT, TimeUnit.MILLISECONDS)
.readTimeout(TIMEOUT, TimeUnit.MILLISECONDS).writeTimeout(TIMEOUT, TimeUnit.MILLISECONDS)
.addInterceptor(new OkHttpSignInterceptor()
.build();
}
public T getRetrofit(String baseUrl, Class<T> proxy) {
Retrofit retrofit = new Retrofit.Builder().client(okHttpClient).baseUrl(baseUrl)
.addConverterFactory(JacksonConverterFactory.create(JsonUtils.getObjectMapper())).build();
return retrofit.create(proxy);
}
}
序列化对象可以做如下的设置:
public class JsonUtils {
private static final String LOCAL_DATE_TIME_PATTERN = "yyyy-MM-dd HH:mm:ss";
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
static {
OBJECT_MAPPER.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
// 忽略空 Bean 转字符串的错误
OBJECT_MAPPER.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
// 忽略反序列化时,对象不存在对应属性的错误
OBJECT_MAPPER.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
OBJECT_MAPPER.setSerializationInclusion(JsonInclude.Include.NON_NULL);
JavaTimeModule timeModule = new JavaTimeModule();
timeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ISO_LOCAL_DATE));
timeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ISO_LOCAL_DATE));
timeModule.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ISO_LOCAL_TIME));
timeModule.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ISO_LOCAL_TIME));
timeModule.addSerializer(LocalDateTime.class,
new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(LOCAL_DATE_TIME_PATTERN)));
timeModule.addDeserializer(LocalDateTime.class,
new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(LOCAL_DATE_TIME_PATTERN)));
OBJECT_MAPPER.registerModule(timeModule);
OBJECT_MAPPER.setDateFormat(new SimpleDateFormat(LOCAL_DATE_TIME_PATTERN));
}
private JsonUtils() {
}
public static ObjectMapper getObjectMapper() {
return OBJECT_MAPPER;
}
}
2 定义代理请求接口和接口方法
我们需要创建一个retrofit对象,构建网络请求载体对象,通常一类方法我们使用一个retrofit,我们以请求发送短信接口为例,该例贯穿如下所有步骤:
/**
* * @Name ContractRetrofitProxy
* * @Description 短信/邮件 调用接口
* * @author tianmaolin
* * @Data 2021/11/18
*/
@RetrofitClient(baseUrl = "${retrofit.message.host}")
public interface MessageRetrofitProxy {
/**
* @Description: 发送短信:
* @Param: [sendMessageParam]
* @Author: tianmaolin
* @Date: 2021/11/24
*/
@POST("/message/send")
Call<CommonMessageResponse<SendMessageResponse>> sendMessage(@Body SendMessageParam sendMessageParam);
}
以上各部分内容含义如下:
@RetrofitClient(baseUrl = "${retrofit.message.host}")
:写在配置文件里的,该请求使用的域名@POST("/message/send")
:该方法的相对路径Call<CommonMessageResponse<SendMessageResponse>>
:Call中的泛型为接收返回值的类型@Body SendMessageParam sendMessageParam
:请求的参数
这样一个通过注解定义的网络请求接口就完成了,当然这里的入参和返回值都是需要我们提前进行定义的
3 创建代理对象并发送请求
这个步骤中我们需要使用上述定义好的接口进行代理对象创建,网络接口方法调用:
/**
* * @Name MessageRetrofitProxyCall
* * @Description
* * @author tianmaolin
* * @Data 2021/11/24
*/
@Slf4j
@Service
public class MessageRetrofitProxyCall extends BaseRetrofitProxy<MessageRetrofitProxy> {
@Resource
private MessageRetrofitProxy messageRetrofitProxy;
@Value("${retrofit.message.host}")
private String domain;
//step1: 创建retrofit对象,并进行动态代理设置
@PostConstruct
public void init() {
messageRetrofitProxy = getRetrofit(domain, MessageRetrofitProxy.class);
}
/**
* @Description:远程调用短信发送接口,返回短信发送结果
* @Param: [sendMessageParam]
* @return: com.ke.merchant.kernel.rpc.retrofit.message.model.response.SendMessageResponse
* @Author: tianmaolin
* @Date: 2021/11/24
*/
public SendMessageResponse remoteSendMessage(SendMessageParam sendMessageParam) {
try {
//step2: 构建具体的网络请求对象
Call<CommonMessageResponse<SendMessageResponse>> result = messageRetrofitProxy.sendMessage(sendMessageParam);
LOGGER.info("send message request,sendMessageParam:{}", sendMessageParam);
//step3: 执行网络请求并获取响应结果
CommonMessageResponse<SendMessageResponse> response = execute(result);
LOGGER.info("send message response,sendMessageParam:{}", response);
if (response != null && ZERO == response.getErrno()) {
LOGGER.info("send message success in message platform,{}", sendMessageParam.toString());
return response.getData();
}
} catch (Exception e) {
LOGGER.error("remote send message error ,sendMessageParam:{}", sendMessageParam, e);
}
return null;
}
/**
* @Description: 调用执行方法
* @Param: [call]
* @return: T
* @Author: tianmaolin
* @Date: 2021/11/24
*/
@Override
protected <T> T execute(Call<T> call) {
try {
Response<T> response = call.execute();
return response.body();
} catch (IOException e) {
throw new ServiceException("MessageRetrofitProxyCall request fail", [添加链接描述](https://tianmaolin.blog.csdn.net/article/details/119702083)e).code("1096").tip("MessageRetrofitProxyCall request fail");
}
}
}
看完完整示例,我们再回过头来回顾下Retrofit的好处:
- 超级解耦 ,接口定义、接口参数、接口回调不再耦合在一起,Retrofit统统帮封装好了:上述的接口定义和参数由MessageRetrofitProxy来实现,执行和回调由MessageRetrofitProxyCall来实现,两个各自是解耦状态。
- 可以配置不同的httpClient来实现网络请求,如OkHttp、HttpClient:在BaseRetrofitProxy中我们可以配置不同的Client
- 支持同步、异步、Rxjava,最佳实践:Retrofit + OkHttp + RxJava。请求的时候如果使用execute就是同步实现,如果使用enqueue就是异步实现
- 可以配置不同反序列化工具类来解析不同的数据,如json、xml。在BaseRetrofitProxy中我们可以配置不同的解析器。
- 请求速度快,使用方便灵活简洁。方便灵活是感受的很彻底,就像调接口一样进行网络请求。性能方面没有做过实验
总的来说就是调用体验很好,所有的请求数据都是可配的。
Retrofit2源码解析
到了最后一个环节,就是底层原理,是什么样的底层原理和源码支撑Retrofit2的功能?这部分就来一探究竟。首先来看下retrofit如何对接口进行代理对象创建,这个其实是retrofit的核心,也就是创建动态代理对象,以上内容分为两步:
public T getRetrofit(String baseUrl, Class<T> proxy) {
Retrofit retrofit = new Retrofit.Builder().client(okHttpClient).baseUrl(baseUrl)
.addConverterFactory(JacksonConverterFactory.create(JsonUtils.getObjectMapper())).build();
return retrofit.create(proxy);
}
1 构建Retrofit对象
构建的大致源码如下,相当于对Retrofit进行一些基础设施的配置:
public static final class Builder {
private final Platform platform;
private @Nullable okhttp3.Call.Factory callFactory;
private @Nullable HttpUrl baseUrl;
private final List<Converter.Factory> converterFactories = new ArrayList<>();
private final List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>();
private @Nullable Executor callbackExecutor;
private boolean validateEagerly;
Builder(Platform platform) {
this.platform = platform;
}
public Builder() {
this(Platform.get());
}
/**
* The HTTP client used for requests.
*
* <p>This is a convenience method for calling {@link #callFactory}.
*/
public Builder client(OkHttpClient client) {
return callFactory(Objects.requireNonNull(client, "client == null"));
}
/**
* Specify a custom call factory for creating {@link Call} instances.
*
* <p>Note: Calling {@link #client} automatically sets this value.
*/
public Builder callFactory(okhttp3.Call.Factory factory) {
this.callFactory = Objects.requireNonNull(factory, "factory == null");
return this;
}
/**
* Set the API base URL.
*
* @see #baseUrl(HttpUrl)
*/
public Builder baseUrl(String baseUrl) {
Objects.requireNonNull(baseUrl, "baseUrl == null");
return baseUrl(HttpUrl.get(baseUrl));
}
/** Add converter factory for serialization and deserialization of objects. */
public Builder addConverterFactory(Converter.Factory factory) {
converterFactories.add(Objects.requireNonNull(factory, "factory == null"));
return this;
}
/**
* Add a call adapter factory for supporting service method return types other than {@link
* Call}.
*/
public Builder addCallAdapterFactory(CallAdapter.Factory factory) {
callAdapterFactories.add(Objects.requireNonNull(factory, "factory == null"));
return this;
}
/**
* The executor on which {@link Callback} methods are invoked when returning {@link Call} from
* your service method.
*
* <p>Note: {@code executor} is not used for {@linkplain #addCallAdapterFactory custom method
* return types}.
*/
public Builder callbackExecutor(Executor executor) {
this.callbackExecutor = Objects.requireNonNull(executor, "executor == null");
return this;
}
/** Returns a modifiable list of call adapter factories. */
public List<CallAdapter.Factory> callAdapterFactories() {
return this.callAdapterFactories;
}
/** Returns a modifiable list of converter factories. */
public List<Converter.Factory> converterFactories() {
return this.converterFactories;
}
/**
* When calling {@link #create} on the resulting {@link Retrofit} instance, eagerly validate the
* configuration of all methods in the supplied interface.
*/
public Builder validateEagerly(boolean validateEagerly) {
this.validateEagerly = validateEagerly;
return this;
}
}
- baseUrl必须指定,这个是理所当然的,设定访问地址的域名
- 如果不设置callFactory,则默认直接
new OkHttpClient()
,如果需要对OkHttpClient
进行详细的设置,需要构建OkHttpClient对象,然后传入 - callbackExecutor是用来将回调传递到UI线程,当然这里设计的比较巧妙,利用platform对象,对平台进行判断,判断主要是利用
Class.forName("")
进行查找,如果是Android平台,会自定义一个Executor对象,并且利用Looper.getMainLooper()实例化一个handler对象,在Executor内部通过handler.post(runnable)
- adapterFactories主要用于对Call进行转化,基本上不需要我们自己去自定义
- converterFactories用于转化数据,例如将返回的responseBody转化为对象等;当然不仅仅是针对返回的数据,还能用于一般备注解的参数的转化例如
@Body
标识的对象做一些操作
以上就是几个基础设施参数。
2 创建代理对象
什么是动态代理呢,其实之前在学习MyBaits和Spring的AOP机制的时候都了解过,这里不再赘述,把我的这两篇Blog贴到这里:【MyBatis学习笔记 四】MyBatis基本运行原理源码解析,以及【Spring学习笔记 六】静态/动态代理实现机制。说白了动态代理就是接口方法的拦截者,在方法执行时增加额外逻辑,而Retrofit2的代理和MyBatis有异曲同工之妙,就是都没有具体的实现类,单纯的进行代理实现。
public <T> T create(final Class<T> service) {
validateServiceInterface(service);
return (T)
Proxy.newProxyInstance(
service.getClassLoader(),
new Class<?>[] {service},
new InvocationHandler() {
private final Platform platform = Platform.get();
private final Object[] emptyArgs = new Object[0];
@Override
public @Nullable 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);
}
args = args != null ? args : emptyArgs;
return platform.isDefaultMethod(method)
? platform.invokeDefaultMethod(method, service, proxy, args)
: loadServiceMethod(method).invoke(args);
}
});
}
1 loadServiceMethod方法
loadServiceMethod方法用来构建一个ServiceMethod对象:
ServiceMethod<?> loadServiceMethod(Method method) {
ServiceMethod<?> result = serviceMethodCache.get(method);
if (result != null) return result;
synchronized (serviceMethodCache) {
result = serviceMethodCache.get(method);
if (result == null) {
result = ServiceMethod.parseAnnotations(this, method);
serviceMethodCache.put(method, result);
}
}
return result;
}
这里调用了parseAnnotations方法进行注解解析
static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);
Type returnType = method.getGenericReturnType();
if (Utils.hasUnresolvableType(returnType)) {
throw methodError(
method,
"Method return type must not include a type variable or wildcard: %s",
returnType);
}
if (returnType == void.class) {
throw methodError(method, "Service methods cannot return void.");
}
return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
}
我们看到这里调用了HttpServiceMethod.parseAnnotations
,整个过程就是在其中进行了注解的解析,整体代码如下:
static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
Retrofit retrofit, Method method, RequestFactory requestFactory) {
boolean isKotlinSuspendFunction = requestFactory.isKotlinSuspendFunction;
boolean continuationWantsResponse = false;
boolean continuationBodyNullable = false;
Annotation[] annotations = method.getAnnotations();
Type adapterType;
if (isKotlinSuspendFunction) {
Type[] parameterTypes = method.getGenericParameterTypes();
Type responseType =
Utils.getParameterLowerBound(
0, (ParameterizedType) parameterTypes[parameterTypes.length - 1]);
if (getRawType(responseType) == Response.class && responseType instanceof ParameterizedType) {
// Unwrap the actual body type from Response<T>.
responseType = Utils.getParameterUpperBound(0, (ParameterizedType) responseType);
continuationWantsResponse = true;
} else {
// TODO figure out if type is nullable or not
// Metadata metadata = method.getDeclaringClass().getAnnotation(Metadata.class)
// Find the entry for method
// Determine if return type is nullable or not
}
adapterType = new Utils.ParameterizedTypeImpl(null, Call.class, responseType);
annotations = SkipCallbackExecutorImpl.ensurePresent(annotations);
} else {
adapterType = method.getGenericReturnType();
}
CallAdapter<ResponseT, ReturnT> callAdapter =
createCallAdapter(retrofit, method, adapterType, annotations);
Type responseType = callAdapter.responseType();
if (responseType == okhttp3.Response.class) {
throw methodError(
method,
"'"
+ getRawType(responseType).getName()
+ "' is not a valid response body type. Did you mean ResponseBody?");
}
if (responseType == Response.class) {
throw methodError(method, "Response must include generic type (e.g., Response<String>)");
}
// TODO support Unit for Kotlin?
if (requestFactory.httpMethod.equals("HEAD") && !Void.class.equals(responseType)) {
throw methodError(method, "HEAD method must use Void as response type.");
}
Converter<ResponseBody, ResponseT> responseConverter =
createResponseConverter(retrofit, method, responseType);
okhttp3.Call.Factory callFactory = retrofit.callFactory;
if (!isKotlinSuspendFunction) {
return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);
} else if (continuationWantsResponse) {
//noinspection unchecked Kotlin compiler guarantees ReturnT to be Object.
return (HttpServiceMethod<ResponseT, ReturnT>)
new SuspendForResponse<>(
requestFactory,
callFactory,
responseConverter,
(CallAdapter<ResponseT, Call<ResponseT>>) callAdapter);
} else {
//noinspection unchecked Kotlin compiler guarantees ReturnT to be Object.
return (HttpServiceMethod<ResponseT, ReturnT>)
new SuspendForBody<>(
requestFactory,
callFactory,
responseConverter,
(CallAdapter<ResponseT, Call<ResponseT>>) callAdapter,
continuationBodyNullable);
}
}
以上整个部分就是就行了ServiceMethod对象的构建,而serviceMethod的调用如下:
2 invoke方法获取call对象
@Override
final @Nullable ReturnT invoke(Object[] args) {
Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
return adapt(call, args);
}
再向下跟看一下adapt方法做了什么:
3 装饰者模式获取具体接口方法代理对象
final class DefaultCallAdapterFactory extends CallAdapter.Factory {
private final @Nullable Executor callbackExecutor;
DefaultCallAdapterFactory(@Nullable Executor callbackExecutor) {
this.callbackExecutor = callbackExecutor;
}
@Override
public @Nullable CallAdapter<?, ?> get(
Type returnType, Annotation[] annotations, Retrofit retrofit) {
if (getRawType(returnType) != Call.class) {
return null;
}
if (!(returnType instanceof ParameterizedType)) {
throw new IllegalArgumentException(
"Call return type must be parameterized as Call<Foo> or Call<? extends Foo>");
}
final Type responseType = Utils.getParameterUpperBound(0, (ParameterizedType) returnType);
final Executor executor =
Utils.isAnnotationPresent(annotations, SkipCallbackExecutor.class)
? null
: callbackExecutor;
return new CallAdapter<Object, Call<?>>() {
@Override
public Type responseType() {
return responseType;
}
@Override
public Call<Object> adapt(Call<Object> call) {
return executor == null ? call : new ExecutorCallbackCall<>(executor, call);
}
};
}
可以看到我们已经确定这个adapt方法返回是:ExecutorCallAdapterFactory.get()
对应代码为:
static final class ExecutorCallbackCall<T> implements Call<T> {
final Executor callbackExecutor;
final Call<T> delegate;
ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) {
this.callbackExecutor = callbackExecutor;
this.delegate = delegate;
}
@Override
public void enqueue(final Callback<T> callback) {
Objects.requireNonNull(callback, "callback == null");
delegate.enqueue(
new Callback<T>() {
@Override
public void onResponse(Call<T> call, final Response<T> response) {
callbackExecutor.execute(
() -> {
if (delegate.isCanceled()) {
// Emulate OkHttp's behavior of throwing/delivering an IOException on
// cancellation.
callback.onFailure(ExecutorCallbackCall.this, new IOException("Canceled"));
} else {
callback.onResponse(ExecutorCallbackCall.this, response);
}
});
}
@Override
public void onFailure(Call<T> call, final Throwable t) {
callbackExecutor.execute(() -> callback.onFailure(ExecutorCallbackCall.this, t));
}
});
}
@Override
public boolean isExecuted() {
return delegate.isExecuted();
}
@Override
public Response<T> execute() throws IOException {
return delegate.execute();
}
@Override
public void cancel() {
delegate.cancel();
}
@Override
public boolean isCanceled() {
return delegate.isCanceled();
}
@SuppressWarnings("CloneDoesntCallSuperClone") // Performing deep clone.
@Override
public Call<T> clone() {
return new ExecutorCallbackCall<>(callbackExecutor, delegate.clone());
}
@Override
public Request request() {
return delegate.request();
}
@Override
public Timeout timeout() {
return delegate.timeout();
}
}
}
可以看出ExecutorCallbackCall仅仅是对Call对象进行封装,类似装饰者模式,只不过将其执行时的回调通过callbackExecutor进行回调到UI线程中去了
3 执行Call调用的方法
我们已经拿到了经过封装的ExecutorCallbackCall类型的call对象,实际上就是我们实际在写代码时拿到的call对象,那么我们一般会执行execute方法,看看源码是怎么做的
@Override
public Response<T> execute() throws IOException {
okhttp3.Call call;
synchronized (this) {
if (executed) throw new IllegalStateException("Already executed.");
executed = true;
call = getRawCall();
}
if (canceled) {
call.cancel();
}
return parseResponse(call.execute());
}
可以看到执行层面还是依赖okhttp3的。
综合以上可以看到我们通过Proxy.newProxyInstance产生的代理类,当调用接口的任何方法时,都会调用InvocationHandler#invoke
方法,在这个方法中可以拿到传入的参数,注解等。retrofit也可以通过同样的方式,在invoke方法里面,拿到所有的参数,注解信息然后就可以去构造RequestBody,再去构建Request,得到Call对象封装后返回。
总结一下
Retrofit2其实是一个非常好的优化体验的框架,其核心还是调用OkHttp框架进行网络接口请求,其最大的好处就是让我们调用网络接口就像调用普通接口一样方便,通过各种注解方便的进行网络请求。通过源码的学习知道了其其实是通过动态代理实现的,动态代理还帮我们搞定了MyBatis框架、AOP,可见知道一种技术原理其收益远大于知道一种简单的框架使用,而一种技术原理又来自于一种技术思想,由此可推,知道一种技术思想>知道一种技术原理>知道一种框架使用。