之前的一篇文章Retrofit初识尝试用了下 Retrofit。说来惭愧,到现在才写这篇文章。由于项目中没有使用的缘故,一直停留在了解的程度。最近自己学习做了个Gank客户端,一点点学习当前主流的技术,今天研究了下 Retrofit 的源码,颇有感触,便记录下来。
使用
关于正常的使用,参考之前的那篇文章。这里再写一下结合 RxJava 的使用。
- 定义接口:
1
2
3
4
5
6
7
8public interface GankService {
@GET("day/{year}/{month}/{day}")
Observable<GankDailyResult> getDailyData(@Path("year") int year, @Path("month") int month, @Path("day") int day);
@GET("data/{type}/{count}/{page}")
Observable<GankCategoryResult> getCategoryData(@Path("type") String type, @Path("count") int count, @Path("page") int page);
} - 初始化 Retrofit,生成代理对象:
1
2
3
4
5
6
7
8Retrofit retrofit = new Retrofit.Builder()
.baseUrl(BASE_GANK_URL)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.client(okHttpClient.build())
.build();
mService = retrofit.create(GankService.class); - 调用接口:
1
2
3
4
5
6
7
8
9
10
11
12GankRequestManager.getInstance().getCategory(type, PAGE_SIZE, mPage)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<GankCategoryResult>() {
@Override
public void accept(GankCategoryResult gankCategoryResult) throws Exception {
mPage++;
if (mView != null) {
mView.showList(type, gankCategoryResult.results);
}
}
});
动态代理
在项目中我们只定义了接口,并没有实现,后面却能直接调用接口,这不得不借助 Java 的动态代理特性。Retrofit 即是使用动态代理来生成代理对象:
1 | public <T> T create(final Class<T> service) { |
通过Proxy.newProxyInstance
来生成代理对象,当调用接口时,会回调到 invoke 方法中,然后生成 OkHttpCall 对象,用于后面真正的发起网络请求。OkHttpCall 是 okhttp 的核心类,这里不做过多讲述。
Android 通过 Handler 回调主线程
普通调用 Retrofit 的形式是这样的:
1 | call.enqueue(new Callback<String>() { |
回调中即是在主线程,我们可以用于更新 UI,这是如何做到的呢?回到 Retrofit 的构建:
1 | public Retrofit build() { |
platform 是如何生成的呢?看到 Retrofit.Builder:
1 | Builder(Platform platform) { |
再来看 Platform:
1 | private static Platform findPlatform() { |
显然,在 Android 中会返回 Android():
1 | static class Android extends Platform { |
看到没, Android platform 会初始化一个绑定 MainLooper 的 Handler,很清晰了。
1 | public void enqueue(final Callback<T> callback) { |
回调中的 Runnable 则是由 Handler 发出的,显然是在主线程了。
CallAdapterFactory
使用 RxJava,会有 RxJava2CallAdapter 类:
1 | final class RxJava2CallAdapter<R> implements CallAdapter<R, Object> { |
通过 adapt 方法来生成目标的对象。使用 RxJava 则是生成 Observable。返回的 observable 只有在发生订阅关系时才会调用请求。
1 |
|
subscribeActual 则是请求真正发生的地方:
1 | protected abstract void subscribeActual(Observer<? super T> observer); |
通过 RxJava2CallAdapter 返回的 observable 是 CallEnqueueObservable 或 CallExecuteObservable:
1 | final class CallEnqueueObservable<T> extends Observable<Response<T>> { |
异步调用 call.enqueue,同步调用 call.execute,这也正是 okhttp 的使用方法,所以 Retrofit 的核心仍是 okhttp,只不过做了更好的封装。
当我们不使用 RxJava 时,会有默认的 ExecutorCallAdapterFactory 类:
1 | final class ExecutorCallAdapterFactory extends CallAdapter.Factory { |
还有一个 DefaultCallAdapterFactory,只有在callbackExecutor == null
的条件才会创建这个类:
1 | final class DefaultCallAdapterFactory extends CallAdapter.Factory { |
通过适配器模式,可是适配各种使用场景,当我们有特定的需求,自定义 CallAdapterFactory 即可。Converter 也是类似的原理。当配置多个 Converter 或者 CallAdapter 时,只有第一个生效。如下代码 RxJava2CallAdapterFactory 生效。
1 | .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) |
设计模式
Retrofit 中使用到的设计模式不可谓不多。
- 建造者模式:这个模式相信大家都很熟悉了。Retrofit 算是比较复杂的一个类了,直接创建一个 Retrofit 将会在碰到这些用不到的方法上困惑,建造者模式提供了一个很好的思路。
- 装饰模式:不管内部多么复杂,在使用时只需要围绕 Retrofit 类就够了。简化了开发者的学习成本,易于使用。另外封装了系统内部类的关系,对内是高内聚的,对外是松耦合的。
- 代理模式:动态代理创建接口实现类,接口的调用都由 OkHttpCall 来执行。
- 工厂模式:源码中可是有不少的 Factory。
- 适配器模式:CallAdapter 通过泛型定义 adapt 方法,开发者可自定义 CallAdapter 实现 adapt 方法来返回自己想要的对象。
1
T adapt(Call<R> call);
当然,不止于这几种模式,只是这几种是我能直观感受到的,再一次感叹 Retrofit 设计的妙处!
设计模式的魅力便在于此,尽管它很复杂,但是开发者使用起来却很简单,也很容易拓展实现自定义需求,好的框架应当如此,努力学习吧!