写在前面
Retrofit 是我用过最友好的网络请求框架。无论是优雅的注解Api,还是强大而灵活的拓展性,都是其流行的一大要素。现如今,它的火爆程度完全不亚于 Rxjava。对于它的成功,api 的友好性是一方面,更深层的原因在于其精妙的解耦,而它的源码更是设计模式的教科书!
我乘机研究了一下源码,同时参考了一些博客,想扒一扒这个轮子的设计理念。
摘要
本文基于 Retrofit 2.0.0-beta4,主要涉及这三个包:
dependencies {
compile 'com.squareup.retrofit2:retrofit:2.0.0-beta4' // Retrofit网络处理
compile 'com.squareup.retrofit2:adapter-rxjava:2.0.0-beta4' // Retrofit的rx解析库
compile 'com.squareup.retrofit2:converter-gson:2.0.0-beta4' // Retrofit的gson库
}
常规的网络请求包含这些步骤,Retrofit 也不例外,但我们不会按照这个顺序来解析源码,而是从使用的角度入手:
- 新建 Request 请求体
- 在 io 线程执行请求(即新建 Thread 或 ThreadPool ),获得 Response
- 从 Response 里解析数据
- 回调到 UI 线程
之后的分析中,你会看到如下设计模式:
- Builder 模式
- 动态代理
- 工厂模式
- 适配器模式
- 装饰者模式
源码分析
0. Retrofit 的配置,Builder 模式
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL) // 必须
.addConverterFactory(GsonConverterFactory.create()) // 可选
.addCallAdapterFactory(RxJavaCallAdapterFactory.create()) // 可选
.client(new OkHttpClient()) // 默认
.callbackExecutor(new MyCallbackExecutor()) // 可选
.build();
private class MyCallbackExecutor implements Executor {
@Override
public void execute(Runnable runnable) {
runnable.run();
}
}
这是一段经典的Retrofit
配置,可以看到支持的配置项有:
- baseUrl
- ConverterFactory: Gson 解析
- CallAdapterFactory: RxJava 支持
- OkHttpClient: 请求内核的替换
- callbackExecutor: 线程切换方式,默认给你 post 到 UI 线程
正如官方所说,这种依赖注入的方式,使得框架的拓展性非常好。
- 感觉 json 不好用,分分钟换成 xml 协议解析。
- 感觉默认的 Callback 不好用,分分钟换成 RxJava;还不好用?再换成 Guava试试。
- …
如图:
顺便一提,Builder
模式是对一系列setXXX()
的封装,常用于复杂对象的构建,这一点在Universal-Image-Loader
里体现的尤为明显。其优点在于build
完之后,该对象便处于不可修改的状态,保证了后续操作的安全性。
1. Api 的使用,动态代理
这里借用一下项目 https://github.com/Dimon94/GanWuMei 里的例子:
配置好retrofit
以后,我们会这样使用它:
// 1. 定义一个 API 接口
public interface RestAPI {
@GET("data/福利/"+ 10 +"/{page}")
Observable<ImageData> getImageData(
@Path("page") int page);
}
// 2. new 出 RestAPI 的对象,并使用之
RestAPI restAPI = retrofit.create(RestAPI.class); // 动态代理,获取代理对象
restAPI.getImageData(page) // 像普通的 RestAPI 的对象一样,直接调用方法
.subcribe(imageData -> onNext(ImageData)); // 2.1 采用 Observable 的异步请求与回调
第一次见可能觉得奇怪,只见接口,不见实现体。这种对客户端隐藏实现体的方式被称为”动态代理”,是代理模式的一种变种。
不了解的童鞋可以先看这篇文章:
公共技术点之 Java 动态代理
我们跟进retrofit.create(Class<?>)
里,可以看到Proxy.newProxyInstance(ClassLoader, Class<?>[], InvocationHandler)
的影子。在匿名类InvocationHandler
中,按照动态代理的尿性,应该调用return method.invoke(delegate, args);
。然而,事实上并没有,因为根本不存在我们定义的RestAPI
的真正实现。事实上,它选择了 new 一个Observable<?>
实例给我们:
// 1. In Retrofit.create()
new InvocationHandler() {
@Override public Object invoke(Object proxy, Method method, Object... args) throws Throwable {
return loadMethodHandler(method).invoke(args); // 我们转到 MethodHandler.invoke(Object...) 里
}
});
// 2. In MethodHandler.java
Object invoke(Object... args) {
return callAdapter.adapt(
new OkHttpCall<>(callFactory, requestFactory, args, responseConverter));
// 这里直接依赖了 OkHttpCall(), 其实还可以通过依赖注入进一步来解耦。
// 我们顺势跳到 RxJavaCallAdapterFactory.SimpleCallAdapter.adapt(Call<R>) 里
}
// 3. In RxJavaCallAdapterFactory.java
static final class SimpleCallAdapter implements CallAdapter<Observable<?>> {
@Override
public <R> Observable<R> adapt(Call<R> call) {
// 我们看到 new 了一个 Observable 出来
return Observable.create(new CallOnSubscribe<>(call))
.flatMap(new Func1<Response<R>, Observable<R>>() {
@Override public Observable<R> call(Response<R> response) {
if (response.isSuccess()) {
return Observable.just(response.body());
}
return Observable.error(new HttpException(response));
}
});
}
}
总结下来就是,通过create()
我们能得到api
的实例,同时在调用方法时给我们提供了Observable
的默认实现。
再另外解释一下第2步:callAdapter.adapt()
直接寻路到RxJavaCallAdapterFactory
里边,你可能觉得奇怪。这是由于,我们在配置的时候有这么一步retrofit.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
(这是可选的)。此时我们的callAdapter
已然是RxJavaCallAdapterFactory.SimpleCallAdapter
了。关于这段逻辑可以自己细看MethodHandler.create()
。
2. Api 的使用,非RxJava的情形,适配器模式
之前说到retrofit.addCallAdapterFactory()
并非必选项,那么不设置的时候,用的就不是Rxjava
的方式,而是内置的Call
与Callback
的回调方式:
// 1. 定义一个 API 接口, 注意到返回值不是 Observable, 而是内置的 Call
public interface RestAPI {
@GET("data/福利/"+ 10 +"/{page}")
Call<ImageData> getImageData(
@Path("page") int page);
}
// 2. new 出 RestAPI 的对象,并使用之
RestAPI restAPI = retrofit.create(RestAPI.class); // 动态代理,获取代理对象
restAPI.getImageData(0) // 像普通的 RestAPI 的对象一样,直接调用方法
.enqueue(new Callback<ImageData>() { // 2.1 采用 Call 的异步请求与回调
@Override
public void onResponse(Call<ImageData> call, Response<ImageData> response) {}
@Override
public void onFailure(Call<ImageData> call, Throwable t) {}
});
你可能注意到了,再看前面的第2步:callAdapter.adapt()
,这次的callAdapter
又是啥呢?
有必要先看一下CallAdapter<T>
这个类:
public interface CallAdapter<T> {
// 转接口:把 Call<R> 转换成 T
<R> T adapt(Call<R> call);
abstract class Factory {
public abstract CallAdapter<?> get(Type returnType, Annotation[] annotations,
Retrofit retrofit);
}
}
// 此前的 Observable 转接口
class SimpleCallAdapter implements CallAdapter<Observable<?>>{
@Override
public <R> Observable<R> adapt(Call<R> call) {...}
}
可以看到,CallAdapter<T>
是为了适配Observable
才加入的,原本内置的OkHttp
请求会返回Call<T>
,为了适配Observable
,我们做了转接。那么现在不配置RxJava
的情况下,我们需要返回的就是Call<T>
,是不是代表这个callAdapter.adapt()
啥都不做呢?你可以姑且这么认为,后边还会提到一些别的细节。
3. 线程切换,切换到 UI 线程,装饰者模式
如果Retrofit
到此为止了,恐怕其友好度会大打折扣吧。你是否注意到回调回来的onResponse()
仍在io线程
,又得用handler.post()
麻烦的要死。作者自然考虑到了这一点。
还记得1. Api 的使用,非RxJava的情形,适配器模式
里有这样一段代码么:
// 2. In MethodHandler.java
Object invoke(Object... args) {
return callAdapter.adapt(
new OkHttpCall<>(callFactory, requestFactory, args, responseConverter));
}
按照上一节所讲,应该可以直接return new OkHttpCall<>(...);
。然而,为了切换线程,作者还是机智的做了一下适配,我们来看ExecutorCallAdapterFactory
:
final class ExecutorCallAdapterFactory extends CallAdapter.Factory {
final Executor callbackExecutor;
ExecutorCallAdapterFactory(Executor callbackExecutor) {
this.callbackExecutor = callbackExecutor;
}
@Override
public CallAdapter<Call<?>> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
return new CallAdapter<Call<?>>() {
// 汗,输入 Call<R> 输出还是 Call<R>,根本没变嘛
@Override public <R> Call<R> adapt(Call<R> call) {
return new ExecutorCallbackCall<>(callbackExecutor, call);
}
};
}
需求是这样的,通过callAdapter.adapt(new OkHttpCall<>(...))
,我们希望得到一个增加了线程切换功能的Call
。
注意重点来了,通常我们为一个类拓展功能有两种方式:
- 继承:子类直接继承
OkHttpCall
,拓展功能。子类直接依赖父类,依赖严重!!! - 组合:新类依赖
OkHttpCall
的接口,并与OkHttpCall
实现同一个接口,然后拓展功能,这便是装饰着模式。和代理模式挺像哦。
这里的ExecutorCallbackCall
便是这样一个装饰者:
// In ExecutorCallAdapterFactory.java
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) {
delegate.enqueue(new Callback<T>() {
@Override public void onResponse(final Call<T> call, final Response<T> response) {
callbackExecutor.execute(new Runnable() { // 关键点在这,execute()做了线程切换
@Override public void run() {
if (delegate.isCanceled()) {
callback.onFailure(call, new IOException("Canceled"));
} else {
callback.onResponse(call, response);
}
}
});
}
@Override public void onFailure(final Call<T> call, final Throwable t) {
callbackExecutor.execute(new Runnable() { // 关键点在这,execute()做了线程切换
@Override public void run() {
callback.onFailure(call, t);
}
});
}
});
}
// 其余接口都只是简单的调用 delegate 的相应方法,就不贴了
}
再看一下execute()
的实现你就懂了,它相当与post()
一个Runnable
出去,于是切换到了UI线程
:
class PlatForm{
static class Android extends Platform {
@Override CallAdapter.Factory defaultCallAdapterFactory(Executor callbackExecutor) {
if (callbackExecutor == null) {
callbackExecutor = new MainThreadExecutor();
}
return new ExecutorCallAdapterFactory(callbackExecutor);
}
static class MainThreadExecutor implements Executor {
private final Handler handler = new Handler(Looper.getMainLooper());
// 看,这里简单的 post 了一下,就切到 UI 线程了
@Override public void execute(Runnable r) {
handler.post(r);
}
}
}
}
当然这个callbackExecutor
也是可以自定义的,你试一下不post
,会不会报io线程更新UI
的错。
总结
源码中设计模式的点大体都覆盖到了,不过关于请求流程上还有细节没讲,放到下一篇里讲好了:
- 新建 Request 请求体(解析注解)
- 在 io 线程执行请求(即新建 Thread 或 ThreadPool ),获得 Response
- 从 Response 里解析数据
总体而言,作者的设计模式玩的很6啊,解耦的也很棒,值得多次回味。
参考资料
源码分析
使用文档
- Jake Wharton 的演讲:用 Retrofit 2 简化 HTTP 请求
- 官方文档