前言
很长一段时间没有写博客,主要原因是工作了感觉没什么时间了(其实是因为懒)。最近在项目中引用了 Retrofit+Rxjava 组合,感觉写的非常刺激。秉着不单单只停留在会用的层次上,还需要对源码进行分析的理念,决定是时候学(zhuang)习(bi)一波了。
此篇博客所需知识
- 对 适配器模式 有一定的了解。
- 会使用 Retrofit 以及 Rxjava
- 知道如何优雅的装逼,故意把一个简单的东西说的很复杂。
Retrofit 使用例子
Retrofit retrofit = new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
// .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.baseUrl("http://gc.ditu.aliyun.com/")
.build();
MyService service = retrofit.create(MyService.class);
Call<City> call = service.getCity("汕头市");
call.enqueue(new Callback<City>() {
@Override
public void onResponse(Call<City> call, Response<City> response) {
Log.e("cat", "response:" + response.body().toString());
}
@Override
public void onFailure(Call<City> call, Throwable t) {
t.printStackTrace();
Log.e("cat", "fail:" + t.getMessage());
}
});
上述代码就是我们使用 Retrofit 的典型代码,对 Retrofit 这个库还不熟悉的请先自行去学习这个库,这篇博客不讲使用,只讲源码,这篇博客不讲使用,只讲源码,这篇博客不讲使用,只讲源码。
源码分析
源码相关知识普及
在看其源码之前,首先有些东西得普及一下,以免跟踪源码的时候有些玩意不知道干什么用的。
首先,Retrofit 最开始会通过 Builder 模式 构造出一个 Retrofit 对象,大部分属性,一旦构造完,便不允许修改。当需要使用的时候,调用 Retrofit.create(Service.class) 初始化我们的业务对象。Retrofit.create() 方法内部使用的是动态代理模式,会拦截我们调用 Service 的每一个方法。为啥要拦截呢?主要是将 Service 接口 中的方法中的注解,封装成一个请求对象 OkHttpCall,并且使用适配器模式来对 OkHttpCall 进行适配,转换成我们需要的类型,例如 Observable。Retrofit 内部会保存有一个 Adapter 以及 Converter 的 list,封装请求对象的时候会遍历这两个集合,寻找适合这个请求的 Adapter 以及 Converter。
上述过程主要是请求的构建,同时 Retrofit 确实也不负责其他功能,其网络请求功能主要还是交给 OkHttp 去实现,依我看来,Retrofit 只是充当了一个粘合剂而已。
三个个重要的对象
1. CallAdapter: 主要是用来对 OkHttpCall 进行适配,常用的就是 RxJavaCallAdapterFactory,构造的时候不指定的话,默认实现是 ExecutorCallAdapterFactory 由工厂生成的。
2. Converter: 用来对后台返回的数据进行转换的转换器,一般后台会返回 json 数据,我们一般会使用 GsonConverterFactory 来进行转换。
3. ServiceMethod: 根据接口中的方法以及注解生成的对象,封装了对应方法的 converter 以及 adapter 等(注意,不同的方法可能有不同的 convertor 以及 adapter)。
4. OkHttpCall:这个东西我很难描述是什么东西,主要是用来执行请求的吧。用过 OkHttp 的应该知道。
构造 Retrofit
构造 Retrofit 采用的是 Builder 设计模式,我们直接点进去 build() 方法看看就好,因为中间的一系列链式调用无非就是一些赋值而已。
Retrofit.Builder.build()
/**
* Create the {@link Retrofit} instance using the configured values.
* <p>
* Note: If neither {@link #client} nor {@link #callFactory} is called a default {@link
* OkHttpClient} will be created and used.
*/
public Retrofit build() {
if (baseUrl == null) {
throw new IllegalStateException("Base URL required.");
}
// callFactory 是构造 Retrofit 时候调用 Buidler.callFactory() 进行赋值的,效果跟 Buidler.clien() 是相同的。
//如果在构造 Retrofit 时没有进行赋值,则默认采用的是 OkHttpClient
okhttp3.Call.Factory callFactory = this.callFactory;
if (callFactory == null) {
callFactory = new OkHttpClient();
}
// callbackExecutor 是构造 Retrofit 时候调用 Buidler.callbackExecutor() 进行赋值的。默认实现是将 Runnbale post 到主线程中执行。
Executor callbackExecutor = this.callbackExecutor;
if (callbackExecutor == null) {
callbackExecutor = platform.defaultCallbackExecutor();
}
// 对 adapterFactories 以及 converterFactories 进行保护性复制,防止由于类的外部持有 adapter 或者 convertor 的 引用从而能能在构造 Retrofit 后仍能从外部对其进行改变。
// Make a defensive copy of the adapters and add the default Call adapter.
List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories);
adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));
// Make a defensive copy of the converters.
List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories);
return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
callbackExecutor, validateEagerly);
}
}
创建完 Retrofit 后就需要实例化我们的业务对象 Service 了,点进去 Retrofit.create() 方法中看下。
初始化业务对象
Retrofit.create()
public <T> T create(final Class<T> service) {
Utils.validateServiceInterface(service);
// 是否在创建业务对象的时候就对接口中的所有方法进行封装和验证。
if (validateEagerly) {
eagerlyValidateMethods(service);
}
//生成代理对象
return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
new InvocationHandler() {
private final Platform platform = Platform.get();
@Override public Object invoke(Object proxy, Method method, 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);
}
if (platform.isDefaultMethod(method)) {
return platform.invokeDefaultMethod(method, service, proxy, args);
}
//上面两个 if 可以省略。下面这几句主要是将方法封装成一个请求对象以及对 okHttpCall 进行适配。
// 根据接口方法中的注解等封装成一个 serviceMethod
ServiceMethod serviceMethod = loadServiceMethod(method);
// 封装成一个请求对象。
OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
// 对 okHttpCall 进行适配,转换成我们需要的返回值类型。
return serviceMethod.callAdapter.adapt(okHttpCall);
}
});
}
这段代码主要是生成我们的业务对象,这是根据我们定义的接口返回来的代理对象,以后我们每次调用业务方法都会被其进行拦截,被其封装成一个请求对象,同时转换成我们需要的类型,这里由于我们没有指定 Adapter,所以采用的是 Retrofit 默认的 ExecutorCallAdapterFactory。
Retrofit.loadServiceMethod
ServiceMethod loadServiceMethod(Method method) {
ServiceMethod result;
// 查看是否有缓存对应 method 的 serviceMethod,有则直接取出,没有就重新生成一个 serviceMethod,并 put 进缓存 map 里面。
synchronized (serviceMethodCache) {
result = serviceMethodCache.get(method);
if (result == null) {
result = new ServiceMethod.Builder(this, method).build();
serviceMethodCache.put(method, result);
}
}
return result;
}
ServiceMethod 的构建 #ServiceMethod.Buidler.build()
public ServiceMethod build() {
// 创建指定 method 的 adapter,内部实现是传入方法的返回值以及注解,遍历 Retrofit 中的 adapter list 进行匹配,通过工厂模式生成 adapter。 responseConverter 同理。
callAdapter = createCallAdapter();
responseType = callAdapter.responseType();
responseConverter = createResponseConverter();
//解析方法的注解,如 @Get @FormUrlEncode 这种注解,注意,不是参数的注解
for (Annotation annotation : methodAnnotations) {
parseMethodAnnotation(annotation);
}
// 解析方法**参数**中的注解
int parameterCount = parameterAnnotationsArray.length;
parameterHandlers = new ParameterHandler<?>[parameterCount];
for (int p = 0; p < parameterCount; p++) {
Type parameterType = parameterTypes[p];
if (Utils.hasUnresolvableType(parameterType)) {
throw parameterError(p, "Parameter type must not include a type variable or wildcard: %s",
parameterType);
}
Annotation[] parameterAnnotations = parameterAnnotationsArray[p];
parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);
}
... 省略了对字段校验的代码
return new ServiceMethod<>(this);
}
上面代码就完成了一个 ServiceMethod 对象的创建,当调用 ServiceMethod.toRequest 方法的时候,就生成了一个请求对象 Request
对 okHttpCall 进行适配
再次回到 Retrofit.create 方法,有如下一段。
return serviceMethod.callAdapter.adapt(okHttpCall);
serviceMethod 我们知道了,callAdapter 是什么呢?如果你在构造 Retrofit 不指定的话,默认实现就是 通过 ExecutorCallAdapterFactory 工厂生产出来的,来看一下这个类先。
final class ExecutorCallAdapterFactory extends CallAdapter.Factory {
final Executor callbackExecutor;
ExecutorCallAdapterFactory(Executor callbackExecutor) {
this.callbackExecutor = callbackExecutor;
}
// serviceMethod 生成 callAdapter 遍历 Retrofit 中的 adapter list 时会调用这个方法,这其实是个工厂方法,只不过这里只有一个实现,目测这破工厂也快倒闭了。RxJavaCallAdapterFactory 中有挺多实现的。
@Override
public CallAdapter<Call<?>> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
if (getRawType(returnType) != Call.class) {
return null;
}
//实际的返回类型,即 Call 或者 Observable 包装的类型。
final Type responseType = Utils.getCallResponseType(returnType);
return new CallAdapter<Call<?>>() {
@Override public Type responseType() {
return responseType;
}
// 将 okHttpCall 进行包装,返回 Retrofit 的默认类型 ExecutorCallbackCall。这段其实就是 Retrofit 中的使用的适配器模式,将 Call 转换成我们想要的类型。
// 如果我们在构造 Retrofit 时指定了 CallAdapter 例如 Rxjava,则 adapt 会返回相应的类型。具体实现可以自行查看 RxJavaCallAdapterFactory
@Override public <R> Call<R> adapt(Call<R> call) {
return new ExecutorCallbackCall<>(callbackExecutor, call);
}
};
}
上述方法就是系统默认的 ExecutorCallAdapterFactory,我们来看看其生成的 CallAdapter 是如何进行适配(adapt) 的。查看 adapt 方法返回的 ExecutorCallbackCall<>(callbackExecutor, call)
static final class ExecutorCallbackCall<T> implements Call<T> {
final Executor callbackExecutor;
final Call<T> delegate;
//这里传入的 delegate 就是 okHttpCall
ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) {
this.callbackExecutor = callbackExecutor;
this.delegate = delegate;
}
// 其实就是把 enqueue 方法代理给 okHttpCall
@Override public void enqueue(final Callback<T> callback) {
if (callback == null) throw new NullPointerException("callback == null");
delegate.enqueue(new Callback<T>() {
@Override public void onResponse(Call<T> call, final Response<T> response) {
callbackExecutor.execute(new Runnable() {
@Override public void run() {
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(new Runnable() {
@Override public void run() {
callback.onFailure(ExecutorCallbackCall.this, t);
}
});
}
});
}
这段没什么好说的,当我们调用 Call.enqueue 的时候,实际上调用的是 okHttpCall 的 enqueue。
总结
最后,再梳理一下。
- 首先,使用建造者模式生成一个 Retrofit。
- 生成我们的业务对象,调用 Retrofit.create 方法,通过动态代理模式返回一个代理对象,以后调用业务对象的任意一个方法, 都会被拦截。将 Service 接口中的每一个方法以及方法中的注解封装成一个请求对象。
- 封装请求对象的具体逻辑是,根据方法的返回值以及注解,遍历 Retrofit 中的 adapter list 以及 convertor list,通过工厂方法返回合适的 adapter 或者 convertor。然后,对 方法的注解进行解析,最后将解析后的结果、adapter 以及 convertor 封装成一个 serviceMethod。
- serviceMethod 封装后,会初始化一个 OkHttpCall ,这个对象就是真正执行请求的对象。
- 最后,调用 serviceMethod 的 adapter 的 adpt 方法对 okHttpCall 进行适配,返回我们想要的类型。例如 Observable。
Retrofit 这个库是一个巧妙的运用了各种设计模式的经典库,有许多地方值得我们学习。虽然能大概看懂其实现,但是要学会其中的思想,仍然需要一定时间的沉淀。同时,这个库比较容易读懂,绝对是我们进阶中级 Android 开发的良品。