Retrofit2已经面世很久了,有很多好的文章分析过,这篇文章我只想记录自己阅读Retrofit 2.3.0源码后的分析过程,如何阅读源码以及分析我觉得是最重要的。
目录
- Retrofit2 使用
- 使用步骤
- 源码分析
- 构建 Retrofit 对象
- 动态代理
- 调用流程
- 自定义 ConverterFactory
- 总结
一、Retrofit2使用
1. 使用步骤
之前做过Gank的客户端,因此直接用Gank网站的请求了。
构建retrofit对象:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://gank.io")
.build();
定义请求interface, 可以把它内部的每一个接口方法看做是一个封装了请求url及参数的方法,其返回值是可执行请求的对象,而在 Retrofit 中默认是 Call 可执行对象,也就是说 call 调用某个方法,如 enqueue 就可以异步执行请求。
public interface ApiService {
@GET("api/data/{type}/{size}/{page}")
Call getArticles(@Path("type") String type, @Path("size") int size,
@Path("page") int page);
}
生成代理对象:
ApiService apiService = retrofit.create(ApiService.class);
获取 call 这个可执行请求的对象,并enqueue异步执行请求:
Call call = apiService.getArticles("Android",10, 1);
call.enqueue(new Callback() {
@Override
public void onResponse(Call call, Response response) {
try {
Log.d(TAG,response.body().string());
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void onFailure(Call call, Throwable t) {
}
});
call.enqueue会执行真正的请求,并且内部会切换到主线程回调到 onResponse 方法内可拿到最终结果。
附上 官方Retrofit使用文档
Retrofit本质上是对okhttp3的封装,其最大的优势就是解耦做的非常好,接下来就从源码角度分析下。
二、源码分析
1. 构建Retrofit对象
构建Retrofit用到了 Builder 模式,很适合自定义一些东西。先走到 baseUrl 方法内看下:
public Builder baseUrl(String baseUrl) {
checkNotNull(baseUrl, "baseUrl == null");
HttpUrl httpUrl = HttpUrl.parse(baseUrl);
if (httpUrl == null)
throw new IllegalArgumentException("Illegal URL: " + baseUrl);
}
return baseUrl(httpUrl);
}
将字符串类型的url解析成 HttpUrl对象,这个对象可以理解为拆分了url的协议、域名、端口、路径等变量并保存在了一个对象中,需要哪个部分就可以随时拿出,最后保存下 httpUrl 变量。
最简单的就是设置一个地址,然后直接调用 build 方法:
public Retrofit build() {
if (baseUrl == null) {
throw new IllegalStateException("Base URL required.");
}
okhttp3.Call.Factory callFactory = this.callFactory;
if (callFactory == null) {
callFactory = new OkHttpClient();
}
Executor callbackExecutor = this.callbackExecutor;
if (callbackExecutor == null) {
callbackExecutor = platform.defaultCallbackExecutor();
}
// Make a defensive copy of the adapters and add the default Call adapter.
List adapterFactories = new ArrayList<>(this.adapterFactories);
adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));
// Make a defensive copy of the converters.
List converterFactories = new ArrayList<>(this.converterFactories);
return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
callbackExecutor, validateEagerly);
}
如果不在构建Retrofit时设置 client 那么就会默认创建一个 OkHttpClient, 前面已经说过 Retrofit 是对 okhttp 的封装而已,本质上还是okhttp 进行请求,而现在只是分析 Retrofit, 因此不打算深入OkHttpClient, 但是根据它的接口类型也能判断是一个 生产okhttp中的Call对象的工厂, 而Call对象就是可执行任务的对象, Retrofit 中也有 Call 导致有点混乱。。需要好好辨别下。
若不设置 callbackExecutor 也会通过 platform.defaultCallbackExecutor() 创建一个默认的,通过看源码发现Retrofit也就支持两个平台,一个是Java8,一个就是Android :
static class Android extends Platform {
@Override public Executor defaultCallbackExecutor() {
return new MainThreadExecutor();
}
@Override CallAdapter.Factory defaultCallAdapterFactory(@Nullable Executor
callbackExecutor) {
if (callbackExecutor == null) throw new AssertionError();
return new ExecutorCallAdapterFactory(callbackExecutor);
}
static class MainThreadExecutor implements Executor {
private final Handler handler = new Handler(Looper.getMainLooper());
@Override public void execute(Runnable r) {
handler.post(r);
}
}
}
它会返回一个 MainThreadExecutor 对象,而它的实现非常简单,就是通过Handler将线程切换到主线程,记住这个 callbackExecutor , 它会在某个地方执行 execute 方法这个时候会切到主线程进行回调。
adapterFactories列表对象可以把它理解为 生产 adapter 的工厂,而 adapter 从名字上来看是适配器,它调用其中的 adapt 方法返回的就是可执行对象!在 Retrofit 里默认的实现就是 Call 对象,并且由于 Retrofit 的神奇解耦,它可以自定义任何CallAdapter 和 Factory,最有名的就是RxJava,其返回的可执行对象变成了Observable而已 ,列表表示可以添加多个 callAdapter工厂, 根据你写的接口方法的返回值判断选择哪个适配器。
默认情况下调用 platform.defaultCallbackExecutor() 创建 ExecutorCallAdapterFactory ,这个类很关键,工厂顾名思义就是生产CallAdapter的,其中的 get 方法返回了一个 CallAdapter 对象,这个对象会在某个关键时刻调用 adapt 从而返回 Call 对象,这个调用流程之后再详解。
converterFactories 转换器工厂,其套路和 adapterFactories 非常类似,它的作用就是将请求后返回的数据转换成你想要的数据结构,应用最广的应该是 GsonConverterFactory , 可以将结果用 Gson 转换成自定义好的数据结构。其默认实现是 BuiltInConverters ,这个默认的工厂返回的 Converter 基本没做什么事情,基本只是把 okhttp 返回给 Retrofit 的 ResponseBody 数据结构返回出去。因此暂时不需要太过在意。
最后以这些对象作为参数传给 Retrofit 构造一个 Retrofit 实例。总结下关键的几个对象:将 url 解析成 HttpUrl 对象并保存供以后使用;在不自定义的情况下默认创建 callFactory, 这个 OkHttpClient 的实例,最终会通过这个工厂生成 okhttp 中的 call 对象来执行真正的请求;callbackExecutor默认实现是 MainThreadExecutor 是用来最后切换到主线程用的;adapterFactories用来生产adapter,默认是 ExecutorCallAdapterFactory,最后会返回 Call 对象;converterFactories默认实现是 BuiltInConverters, 基本上就是将 okhttp 返回的 ResponseBody 返回出去。
2. 动态代理
动态代理这个可以说是 Retrofit 最精华的地方了。再此之前需要理解反射和动态代理,推荐两篇不错的文章:反射、动态代理。
构建完 Retrofit 对象后,需要调用 retrofit.create(ApiService.class), 生成 ApiService 接口对应的动态代理对象。
public T create(final Class service) {
// ......
return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class>[] { service },
new InvocationHandler() {
// 获取目前的平台,对于我们来说就是Android平台
private final Platform platform = Platform.get();
@Override public Object invoke(Object proxy, Method method, @Nullable Object[] args)
throws Throwable {
// ......
// 构建 接口方法 对象,此对象主要是解析注解并拼接成请求所需的参数。
// 它是最后用来给okhttp使用并真正发送请求。
ServiceMethod
部分没啥用的代码省略了,剩下的都是关键代码,并且加了注释。这里再说下动态代理相关的,create 方法返回值是第一部分的使用步骤中的 apiService, 它是动态代理生成的对象。调用动态代理对象的方法后会调到 invoke 方法,返回值对应使用步骤中的 call 对象。
进入 loadServiceMethod 方法,可以看到对 serviceMethod 会有一个缓存,一个方法只会解析一次,之后重复利用。然后通过 build 构建一个 ServiceMethod 对象。
public ServiceMethod build() {
// 跟进 createCallAdapter 可以看到它返回的 CallAdapter 是根据方法的返回类型,
// 如本文使用步骤中返回类型是 Call,那么就会返回之前已创建过的默认 CallAdapter
callAdapter = createCallAdapter();
// 返回结果类型,对于 Retrofit 默认返回结果类型是 ResponseBody,
// 因此在阅读源码时直接把 responseType 看做 ResponseBody 类型。
responseType = callAdapter.responseType();
// ......
// 跟进 createResponseConverter 方法可以看到它是根据 responseType 返回对应的 Converter。
responseConverter = createResponseConverter();
// 遍历解析方法上的注解如 @GET、@POST等(拆分注解中的字符串,将参数名记录)
for (Annotation annotation : methodAnnotations) {
parseMethodAnnotation(annotation);
}
// ......
for (int p = 0; p < parameterCount; p++) {
Type parameterType = parameterTypes[p];
// ......
Annotation[] parameterAnnotations = parameterAnnotationsArray[p];
// ......
// 遍历解析形参的注解,如@Path、@Query等.
parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);
}
// ......
return new ServiceMethod<>(this);
}
其实 ServiceMethod 不是理解 Retrofit 的关键,只需知道它封装了解析注解逻辑和记录参数。
直接看下 OkhttpCall 的enqueue方法
@Override public void enqueue(final Callback callback) {
okhttp3.Call call;
synchronized (this) {
// ......
// 这里的 call 就是 OkHttp 的 call
call = rawCall = createRawCall();
}
// ......
// okhttp 的 call 异步执行并回调,注意这里依然在异步线程中。
call.enqueue(new okhttp3.Callback() {
@Override public void onResponse(okhttp3.Call call, okhttp3.Response rawRespon
throws IOException {
Response response;
try {
response = parseResponse(rawResponse);
} catch (Throwable e) {
callFailure(e);
return;
}
callSuccess(response);
}
// ....
private void callSuccess(Response response) {
try {
callback.onResponse(OkHttpCall.this, response);
} catch (Throwable t) {
t.printStackTrace();
}
}
});
}
最后一步通过 callAdapter.adapt(okHttpCall) 返回一个最终可执行对象,这里我们都是看默认的,因此 callAdapter 是 ExecutorCallAdapterFactrory 中 get 获取的 callAdapter, 其 adapter 方法返回的是 ExecutorCallbackCall 也是实现了 Call 接口,之前说过此对象是 okHttpCall 的代理对象,因此传入 okHttpCall 实例,而内部干的最关键的一件事就是在异步执行请求完成后通过 callBackExecutor(之前早已准备好的Handler切换) 切换到主线程。
3. 调用流程
绿色线框代表开发者使用 Retrofit 需要做的几步。
Retrofit 内部有大量的设计模式,设计的非常巧妙,多看源码也能提高我们的代码设计能力。build 阶段用了构造者模式,create 用了动态代理模式,CallAdapterFactory 和 ConverterFactory 用了适配器模式,等等。
三、 自定义ConverterFactory
为了加深对Retrofit的理解以及体会它的好用程度,写了一个自定义ConverterFactory。
public final class StringConverterFactory extends Converter.Factory {
private final static String TAG = "StringConverterFactory";
@Nullable
@Override
public Converter responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
if(type == String.class){
return StringResponseConverter.INSTANCE;
}
return null;
}
@Nullable
@Override
public Converter, RequestBody> requestBodyConverter(Type type, Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
if(type == RequestBody.class){
return StringRequestBodyConverter.INSTANCE;
}
return null;
}
final static class StringResponseConverter implements Converter {
final static StringResponseConverter INSTANCE = new StringResponseConverter();
@Override
public String convert(ResponseBody value) throws IOException {
Log.d(TAG, value.toString());
return value.string();
}
}
final static class StringRequestBodyConverter implements Converter {
final static StringRequestBodyConverter INSTANCE = new StringRequestBodyConverter();
@Override
public RequestBody convert(RequestBody value) throws IOException {
Log.d(TAG, "no change, hahaha...");
return value;
}
}
}
支持String的泛型参数,其实内部也就是把 ResponseBody 提前转成 String 并打印而已。在使用的时候需要在构建 Retrofit 时添加:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://gank.io")
.addConverterFactory(new StringConverterFactory())
.build();
四、总结
最后对 Retrofit 做一个总结 。Retrofit 是对 okhttp3 的封装,其最大的特性就是解耦,你可以自定义很多你想要的东西,最知名的就是 RxJava 的配合使用。
首先调用 build 方法创建生产 okhttp3 的 call 对象的callFactory、创建用来切换线程的 callBackExecutor、创建生产 CallAdapter 的 CallAdapterFactory、创建生产 Converter 的 ConverterFactory。
接着调用 retrofit.create 创建动态代理对象,调用接口方法会触发 invoke 方法,invoke 内创建
ServiceMethod并做了注解解析、创建OkhttpCall(对okhttp3的call进一步封装),最后通过 CallAdapter.adapt 方法返回可执行对象默认是 ExecutorCallBackCall。
最后调用 call.enqueue 后 okhttp3将请求 ServiceMethod 解析好的url和参数,最终返回结果会被 Converter.convert 解析成你想要的数据模型(默认是ResponseBody),最后通过 callBackExecutor.execute 切换到主线程将数据回调给开发者。
ps:做完 Retrofit 源码分析后,还想看下 RxJava 的源码,为了更深入理解 RxJava 和 Retrofit 共同使用的原理。最近想到一句话感觉蛮有意思的:当你不知道做什么的时候,去看看源码或官方文档吧。