万丈高楼平地起,先对Retrofit进行一些简单封装,方便外部调用(熟悉的同学可以忽略):
public class RetrofitFactory {
private static OkHttpClient sOkHttpClient;
private static Retrofit sRetrofit;
// 创建网络请求Observable
public static RetrofitInterface createRequest() {
return getRetrofit().create(RetrofitInterface.class);
}
// 配置Retrofit,这里的单例仅作简单示意
private synchronized static Retrofit getRetrofit() {
if (sRetrofit == null) {
sRetrofit = new Retrofit.Builder()
.baseUrl("http://test.com") // 对应服务端的host
.client(getHttpClient())
.addConverterFactory(GsonConverterFactory.create()) // 这里还结合了Gson
.addCallAdapterFactory(RxJava2CallAdapterFactory.create()) // 把Retrofit请求转化成RxJava的Observable
.build();
}
return sRetrofit;
}
// OkHttpClient的配置初始化就省略了,这个不是重点
private synchronized static OkHttpClient getHttpClient() { ... };
...
}
getRemoteData是RetrofitInterface接口之一,示例如下:
public interface RetrofitInterface {
// 获取远程服务器的数据
@GET("test/getData")
Observable<DataClass> getRemoteData();
}
基础设施搞定后,我们要发起网络请求,一般会这样写:
// 创建网络请求的Observable
Observable<DataClass> remoteDataObservable = RetrofitFactory.createRequest()
.getRemoteData()
.subscribeOn(Schedulers.io()) // 订阅触发后,在子线程中进行请求
.map(dataResponse -> {
DataClass remoteData = dataResponse;
// 假装对返回数据做了一些处理
// ...
return remoteData;
})
.observeOn(AndroidSchedulers.mainThread()); // 在UI线程中暗中观察并及时消费
// 然后在需要的地方订阅刚才创建的可观察对象,返回一个Disposable对象,后面我们会用到
Disposable disposable = remoteDataObservable.subscribe(remoteData -> {
// 在回调中,UI线程食用远程数据
// ...
});
需要注意的是,只有在调用Observable的subscribe方法时,网络请求才会触发,订阅即触发。
就这样,整个网络请求的过程就完毕了,完结撒花……个铲铲。
正文开始了:
如果在请求过程中,UI层destroy了怎么办,不及时取消订阅,可能会造成内存泄漏。因此,CompositeDisposable就上场了,它可以对我们订阅的请求进行统一管理。
大致三步走:
1、在UI层创建的时候(比如onCreate之类的),实例化CompositeDisposable;
2、把subscribe订阅返回的Disposable对象加入管理器;
3、UI销毁时清空订阅的对象。
private CompositeDisposable mCompositeDisposable;
// when create UI
mCompositeDisposable = new CompositeDisposable();
// when request data
if (mCompositeDisposable != null && !mCompositeDisposable.isDisposed()) {
mCompositeDisposable.add(disposable);
}
// when destroy UI
if (mCompositeDisposable != null) {
mCompositeDisposable.clear(); // clear时网络请求会随即cancel
mCompositeDisposable = null;
}
这样我们就可以管理脱缰的网络请求了,相当于将它与UI的生命周期绑定。只要稍稍将上述模板封装一哈,就比较方便了。比如add操作可以封装一个方法,每次网络请求时add一发就好。
对于MVP架构的项目,CompositeDisposable完全可以封装到Presenter当中。这里就不展开了。
多说两句:
那么,Disposable是什么东西呢?我们查看Observable源码,发现上述的subscribe方法实际上返回了一个Disposable对象:
@CheckReturnValue
@SchedulerSupport(SchedulerSupport.NONE)
public final Disposable subscribe(Consumer<? super T> onNext, Consumer<? super Throwable> onError) {
return subscribe(onNext, onError, Functions.EMPTY_ACTION, Functions.emptyConsumer());
}
第一次见到这个单词,我也是很疑惑的,查下字典发现,disposable有一次性使用,可任意处置的意思(形容词、名词都有),其词根应该是动词dispose,有安排,处置的意思。
看来RxJava的作者大佬们表达得非常到位,你看我们的网络请求不就是天天被安排的对象么?
再一看Disposable的源码,其实就是一个非常简单的接口类,窥探其方法,含义就更加清晰了:
package io.reactivex.disposables;
/**
* Represents a disposable resource.
*/
public interface Disposable {
/**
* Dispose the resource, the operation should be idempotent.
*/
void dispose(); // 安排!
/**
* Returns true if this resource has been disposed.
* @return true if this resource has been disposed
*/
boolean isDisposed(); // 安排完了没?
}
CompositeDisposable的clear方法内部,实际上就会调用Disposable的dispose方法。
这里我们大致可以猜出一二,Retrofit肯定是重写了dispose方法,在其中把网络请求的call给cancel掉了,这哈被安排得明明白白的。
在刚学习Rxjava2时,我们都知道 通过调用 Disposable 的 dispose()方法能主动断开 Observable和Observer之间的连接,但是能不能通过这个类去主动取消网络请求呢? 答案当然是 :可以的!!
来看看Retrofit2在搭配Rxjava2时的基本配置
点开RxJava2CallAdapterFactory.create()我们看看源码:
get方法中会返回RxJava2CallAdapter,继续点击源码:
Observable/Flowable
没错,我们看到了我们熟悉的Observable和Flowable这就是Retrofit2+Rxjava2搭配所返回的Observable/Flowable 并绑定了Call类。我们看看这个Observable/Flowable到底和Call类干了什么勾当:点开同步执行的CallEnqueueObservable:
发现在dispose方法里已经调用了 call.cancle();
至此困扰了两天的疑惑就此解开!!