Rxjava解除訂閱三部曲:
前言
最近在維護(hù)老舊網(wǎng)絡(luò)庫(kù)的時(shí)候,發(fā)現(xiàn)網(wǎng)絡(luò)庫(kù)底層運(yùn)用到了Rxjava,而最近湊巧又給app加上了leakcanary檢測(cè)內(nèi)存泄漏唧躲,發(fā)現(xiàn)除了網(wǎng)絡(luò)庫(kù)的Rxjava泄漏之外干厚,還有些業(yè)務(wù)上濫用的Rxjava也存在泄漏的情況。有問題咱就得想辦法解決敬察,這個(gè)老舊的網(wǎng)絡(luò)庫(kù)有點(diǎn)年歲了,寫的是真不咋樣,奈何app里還有大量引用致燥,該維護(hù)的還是得維護(hù)。Rxjava作為近幾年非常流行的一個(gè)三方庫(kù)怖侦,功能就不用多說了篡悟,誰(shuí)用誰(shuí)知道。
正文
Rxjava是好用匾寝,可用不好很容易造成內(nèi)存泄漏搬葬。而且理論上Rxjava的每個(gè)操作符都可能會(huì)造成內(nèi)存泄漏。舉個(gè)例子艳悔,我們用Rx進(jìn)行網(wǎng)絡(luò)請(qǐng)求急凰,然后訂閱在主線程進(jìn)行ui更新。網(wǎng)絡(luò)請(qǐng)求是在分線程執(zhí)行,而且有延遲抡锈。當(dāng)請(qǐng)求沒有返回時(shí)疾忍,我們將這個(gè)頁(yè)面關(guān)閉了,后續(xù)分線程數(shù)據(jù)回來執(zhí)行ui更新床三,而Rx還持有外部類的引用一罩,這就造成了內(nèi)存泄漏。
解決的辦法Rx本身就給提供撇簿,這就是我們要講的第一種解除訂閱的方法:
- Rxjava提供的
Dispose.dispose();
1.Dispose.dispose()
這是Rxjava本身提供的一種接觸訂閱的方式聂渊,使用很簡(jiǎn)單,在頁(yè)面關(guān)閉的時(shí)候四瘫,或者在需要的時(shí)候調(diào)用下dispose()
方法就可以了汉嗽。
如果最后的訂閱者是Consumer
,那么會(huì)有一個(gè)返回值Dispose
找蜜。那么在需要的時(shí)候饼暑,就可以調(diào)用Dispose.dispose()
。但往往我們使用Rxjava的時(shí)候洗做,都需要對(duì)正常返回和異常返回做些通用處理弓叛,使用的往往是Observer
,這樣做的結(jié)果就是沒有返回值竭望。
其實(shí)Observer
也已經(jīng)給我們準(zhǔn)備好了解除訂閱的方式邪码,我們不妨看下Observer
的源碼:
public interface Observer<T> {
void onSubscribe(@NonNull Disposable d);
void onNext(@NonNull T t);
void onError(@NonNull Throwable e);
void onComplete();
}
有個(gè)方法onSubscribe(@NonNull Disposable d)
,有一個(gè)Dispose
的參數(shù)咬清,那么我們就可以接收這個(gè)變量闭专,在在需要的時(shí)候調(diào)用Dispose.dispose()
。
這個(gè)方法onSubscribe
意味著當(dāng)subscribe
方法被調(diào)用之前旧烧,就會(huì)拿到Dispose
句柄影钉,此時(shí)Rxjava任何一個(gè)相關(guān)的操作符處理都還未執(zhí)行,調(diào)用dispose()
方法后掘剪,完成解除訂閱平委。
擴(kuò)展: CompositeDisposable
上面講的是針對(duì)單個(gè)Dispose進(jìn)行訂閱解除,可往往實(shí)際使用中夺谁,我們可希望看到一堆Dispose的成員變量在頁(yè)面銷毀的時(shí)候扎堆解除訂閱廉赔。這時(shí)候就需要CompositeDisposable
,簡(jiǎn)單的理解匾鸥,就是可以對(duì)Dispose進(jìn)行批量的處理蜡塌,類似于List集合,其內(nèi)部實(shí)現(xiàn)方法也很類似勿负,包括add馏艾,addAll,delete,remove琅摩,clear铁孵,dispose,isDisposed
房资。
方法使用都很簡(jiǎn)單蜕劝,我們把Dispose1,Dispose2志膀,Dispose3使用add
方法熙宇,添加到CompositeDisposable
中鳖擒,在頁(yè)面銷毀時(shí)調(diào)用dispose
進(jìn)行批量解除溉浙。
這里對(duì)dispose
和clear方法進(jìn)行單獨(dú)說明下,dispose
執(zhí)行后蒋荚,會(huì)改變CompositeDisposable
的狀態(tài)為disposed戳稽,即已完成訂閱解除狀態(tài),而clear
則只會(huì)批量解除訂閱期升,不會(huì)改變整個(gè)CompositeDisposable
的disposed狀態(tài)惊奇。我們看下源碼就知道了:
dispose方法:
而clear方法:
差異就在紅箭頭那邊。
而其他的方法都要去判斷disposed狀態(tài)播赁,已經(jīng)disposed的直接return颂郎,不會(huì)繼續(xù)執(zhí)行。
2.RxLifeCycle
接下來開始容为,就是比較騷的操作了乓序。RxLifeCycle,顧名思義就是對(duì)Rxjava生命周期管理坎背,也就是意味著替劈,Rxjava長(zhǎng)大,已經(jīng)學(xué)會(huì)了自己該何時(shí)進(jìn)行解除訂閱得滤。github直達(dá)鏈接陨献。
雖然沒有中文文檔,但摸索起來也不困難懂更,首先添加核心依賴:
implementation 'com.trello.rxlifecycle3:rxlifecycle:3.1.0'
implementation 'com.trello.rxlifecycle3:rxlifecycle-components:3.1.0'
RxLifeCycle使用需要繼承RxAppCompatActivity
眨业,F(xiàn)ragment也需要繼承RxFragment
,當(dāng)然還有一些其他擴(kuò)展沮协,比如RxDialogFragment
等龄捡,大家自己去體驗(yàn)吧。
使用起來也是很簡(jiǎn)單皂股,直接看代碼吧:
Observable.just(1)
.compose(this.<Integer>bindToLifecycle())
.subscribe();
或者:
Observable.just(1)
.compose(this.<Integer>bindUntilEvent(ActivityEvent.DESTROY))
.subscribe();
核心使用就這兩個(gè)方法墅茉,這倆方法也是有些許的區(qū)別。
bindToLifecycle():自動(dòng)識(shí)別在合適的生命周期內(nèi)解除綁定。
bindUntilEvent(ActivityEvent):在指定的生命周期內(nèi)解除綁定就斤。
對(duì)于bindUntilEvent(ActivityEvent)
很容易理解悍募,指定一個(gè)生命周期解除綁定,但對(duì)于bindToLifecycle()
如何自動(dòng)識(shí)別生命周期有些疑問洋机,我們不妨寫個(gè)demo測(cè)試下效果如何:
private void test() {
subscribe = Observable.interval(0, 2, TimeUnit.SECONDS)
.map(new Function<Long, Long>() {
@Override
public Long apply(Long aLong) throws Exception {
Log.d(TAG, "當(dāng)前發(fā)射數(shù)值:" + aLong);
return aLong;
}
})
.compose(this.<Long>bindToLifecycle())
.subscribe(new Consumer<Long>() {
@Override
public void accept(Long aLong) throws Exception {
Log.d(TAG, "當(dāng)前接收數(shù)值:" + aLong);
}
});
}
每個(gè)2s發(fā)射一個(gè)數(shù)值坠宴,無限的發(fā),調(diào)用bindToLifecycle()
绷旗,然后我們?cè)?code>onCreate方法調(diào)用喜鼓,最終打印效果:
D/zdu_Rxdemo: onCreate,subscribe.isDisposed():false
D/zdu_Rxdemo: 當(dāng)前發(fā)射數(shù)值:0
D/zdu_Rxdemo: 當(dāng)前接收數(shù)值:0
D/zdu_Rxdemo: onStart,subscribe.isDisposed():false
D/zdu_Rxdemo: onResume,subscribe.isDisposed():false
D/zdu_Rxdemo: 當(dāng)前發(fā)射數(shù)值:1
D/zdu_Rxdemo: 當(dāng)前接收數(shù)值:1
D/zdu_Rxdemo: 當(dāng)前發(fā)射數(shù)值:2
D/zdu_Rxdemo: 當(dāng)前接收數(shù)值:2
D/zdu_Rxdemo: onPause,subscribe.isDisposed():false
D/zdu_Rxdemo: 當(dāng)前發(fā)射數(shù)值:3
D/zdu_Rxdemo: 當(dāng)前接收數(shù)值:3
D/zdu_Rxdemo: onStop,subscribe.isDisposed():false
D/zdu_Rxdemo: onDestroy,subscribe.isDisposed():true
在onDestroy
方法中自動(dòng)解除訂閱了,而代碼中并沒有主動(dòng)去調(diào)用dispose方法衔肢,可見自動(dòng)解除訂閱生效了庄岖。
那如果在onStart
方法訂閱的話,解除訂閱的生命周期又不一樣了:
D/zdu_Rxdemo: onStart,subscribe.isDisposed():false
D/zdu_Rxdemo: 當(dāng)前發(fā)射數(shù)值:0
D/zdu_Rxdemo: 當(dāng)前接收數(shù)值:0
D/zdu_Rxdemo: onResume,subscribe.isDisposed():false
D/zdu_Rxdemo: 當(dāng)前發(fā)射數(shù)值:1
D/zdu_Rxdemo: 當(dāng)前接收數(shù)值:1
D/zdu_Rxdemo: 當(dāng)前發(fā)射數(shù)值:2
D/zdu_Rxdemo: 當(dāng)前接收數(shù)值:2
D/zdu_Rxdemo: onPause,subscribe.isDisposed():false
D/zdu_Rxdemo: onStop,subscribe.isDisposed():true
D/zdu_Rxdemo: onDestroy,subscribe.isDisposed():true
在onStop
生命周期內(nèi)就被解除訂閱了角骤,那我們?cè)?code>onResume中訂閱的話隅忿,是不是就會(huì)在onPause
中解除訂閱了呢?事實(shí)上確實(shí)是這樣的邦尊,日志就不打印了背桐,我們直接去看源碼實(shí)現(xiàn)。
剛剛我們調(diào)用的是RxAppCompatActivity
的兩個(gè)方法:
@NonNull
@CheckResult
public final <T> LifecycleTransformer<T> bindUntilEvent(@NonNull ActivityEvent event) {
return RxLifecycle.bindUntilEvent(this.lifecycleSubject, event);
}
@NonNull
@CheckResult
public final <T> LifecycleTransformer<T> bindToLifecycle() {
return RxLifecycleAndroid.bindActivity(this.lifecycleSubject);
}
暫且現(xiàn)不管this.lifecycleSubject
是什么蝉揍,我們繼續(xù)向下看源碼:
public static <T, R> LifecycleTransformer<T> bindUntilEvent(@Nonnull final Observable<R> lifecycle,
@Nonnull final R event) {
checkNotNull(lifecycle, "lifecycle == null");
checkNotNull(event, "event == null");
return bind(takeUntilEvent(lifecycle, event));
}
private static <R> Observable<R> takeUntilEvent(final Observable<R> lifecycle, final R event) {
return lifecycle.filter(new Predicate<R>() {
@Override
public boolean test(R lifecycleEvent) throws Exception {
return lifecycleEvent.equals(event);
}
});
}
bindUntilEvent
就很明確了链峭,底層用了filter
過濾操作符,過濾了非指定生命周期又沾。但生命周期是怎么下發(fā)下來的呢弊仪?最后再做解釋。
public static <T> LifecycleTransformer<T> bindActivity(@NonNull Observable<ActivityEvent> lifecycle) {
return RxLifecycle.bind(lifecycle, ACTIVITY_LIFECYCLE);
}
bindToLifecycle
的底層源碼最終跟takeUntilEvent
的底層源碼一致捍掺,都指向了
RxLifecycle.bind
方法撼短。我們就看下bind
方法到底是執(zhí)行了什么?
先看bindUntilEvent
:
public static <T, R> LifecycleTransformer<T> bind(@Nonnull final Observable<R> lifecycle) {
return new LifecycleTransformer<>(lifecycle);
}
new了一個(gè)LifecycleTransformer
挺勿,在看下內(nèi)部實(shí)現(xiàn):
public final class LifecycleTransformer<T> implements ObservableTransformer<T, T>,
FlowableTransformer<T, T>,
SingleTransformer<T, T>,
MaybeTransformer<T, T>,
CompletableTransformer
{
final Observable<?> observable;
LifecycleTransformer(Observable<?> observable) {
checkNotNull(observable, "observable == null");
this.observable = observable;
}
@Override
public ObservableSource<T> apply(Observable<T> upstream) {
return upstream.takeUntil(observable);
}
@Override
public Publisher<T> apply(Flowable<T> upstream) {
return upstream.takeUntil(observable.toFlowable(BackpressureStrategy.LATEST));
}
@Override
public SingleSource<T> apply(Single<T> upstream) {
return upstream.takeUntil(observable.firstOrError());
}
@Override
public MaybeSource<T> apply(Maybe<T> upstream) {
return upstream.takeUntil(observable.firstElement());
}
@Override
public CompletableSource apply(Completable upstream) {
return Completable.ambArray(upstream, observable.flatMapCompletable(Functions.CANCEL_COMPLETABLE));
}
}
原來是一個(gè)實(shí)現(xiàn)ObservableTransformer
等接口的類曲横,到這里也明白了為什么RxLifeCycle要用compose
操作符,并且其內(nèi)部實(shí)現(xiàn)使用了takeUntil
操作符不瓶,在符合條件后禾嫉,打斷上游鏈。這也說明了蚊丐,我們要在訂閱前一刻執(zhí)行這個(gè)自動(dòng)解除訂閱打斷上游鏈熙参,而對(duì)下游鏈沒有作用。
再看下bindToLifecycle
的bind
實(shí)現(xiàn)麦备,與takeUntilEvent
稍微有點(diǎn)不同的是它要自動(dòng)判斷生命周期:
@NonNull
@CheckResult
public static <T> LifecycleTransformer<T> bindActivity(@NonNull Observable<ActivityEvent> lifecycle) {
return RxLifecycle.bind(lifecycle, ACTIVITY_LIFECYCLE);
}
而ACTIVITY_LIFECYCLE
則是一個(gè)switch取值:
private static final Function<ActivityEvent, ActivityEvent> ACTIVITY_LIFECYCLE = new Function<ActivityEvent, ActivityEvent>() {
public ActivityEvent apply(ActivityEvent lastEvent) throws Exception {
switch(lastEvent) {
case CREATE:
return ActivityEvent.DESTROY;
case START:
return ActivityEvent.STOP;
case RESUME:
return ActivityEvent.PAUSE;
case PAUSE:
return ActivityEvent.STOP;
case STOP:
return ActivityEvent.DESTROY;
case DESTROY:
throw new OutsideLifecycleException("Cannot bind to Activity lifecycle when outside of it.");
default:
throw new UnsupportedOperationException("Binding to " + lastEvent + " not yet implemented");
}
}
};
這就印證了我們之前的demo孽椰,在create的時(shí)候返回是ActivityEvent.DESTORY昭娩,對(duì)應(yīng)START返回的就是STOP生命周期等等。
public static <T, R> LifecycleTransformer<T> bind(@Nonnull Observable<R> lifecycle, @Nonnull final Function<R, R> correspondingEvents) {
checkNotNull(lifecycle, "lifecycle == null");
checkNotNull(correspondingEvents, "correspondingEvents == null");
return bind(takeUntilCorrespondingEvent(lifecycle.share(), correspondingEvents));
}
private static <R> Observable<Boolean> takeUntilCorrespondingEvent(final Observable<R> lifecycle,
final Function<R, R> correspondingEvents) {
return Observable.combineLatest(
lifecycle.take(1).map(correspondingEvents),
lifecycle.skip(1),
new BiFunction<R, R, Boolean>() {
@Override
public Boolean apply(R bindUntilEvent, R lifecycleEvent) throws Exception {
return lifecycleEvent.equals(bindUntilEvent);
}
})
.onErrorReturn(Functions.RESUME_FUNCTION)
.filter(Functions.SHOULD_COMPLETE);
}
這個(gè)takeUntilCorrespondingEvent
就是生命周期判斷黍匾,內(nèi)部使用了combineLatest
操作符栏渺,簡(jiǎn)單的說就是該操作符接收多個(gè)Observable以及一個(gè)函數(shù)作為參數(shù),并且函數(shù)的簽名為這些Observable發(fā)射的數(shù)據(jù)類型锐涯。當(dāng)以上的任意一個(gè)Observable發(fā)射數(shù)據(jù)之后磕诊,會(huì)去取其它Observable 最近一次發(fā)射的數(shù)據(jù),回調(diào)到函數(shù)當(dāng)中纹腌,但是該函數(shù)回調(diào)的前提是所有的Observable都至少發(fā)射過一個(gè)數(shù)據(jù)項(xiàng)霎终。
看不懂上面的沒有關(guān)系,takeUntilCorrespondingEvent
的作用就是篩選過濾生命周期升薯,那么問題又來了莱褒,這個(gè)生命周期到底是哪發(fā)射來的呢?
其實(shí)是用了 BehaviorSubject
覆劈。在特定條件下保礼,Subject
既可以發(fā)送事件,也可以接收事件责语,而BehaviorSubject
接收到訂閱前的最后一條數(shù)據(jù)和訂閱后的所有數(shù)據(jù)。BehaviorSubject
在Activity的每個(gè)生命周期都發(fā)射了一個(gè)生命周期事件:
@CallSuper
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.lifecycleSubject.onNext(ActivityEvent.CREATE);
}
@CallSuper
protected void onStart() {
super.onStart();
this.lifecycleSubject.onNext(ActivityEvent.START);
}
......
其他生命周期也類似目派,不再貼出來了坤候。如此,完整的RxLifeCycle源碼執(zhí)行分析到此為止企蹭。
結(jié)語(yǔ)
RxLifeCycle做為自動(dòng)解除綁定的一個(gè)三方庫(kù)白筹,源碼實(shí)現(xiàn)比較簡(jiǎn)單易讀,一定程度上可以幫助我們解決Rxjava內(nèi)存泄漏的問題谅摄,但不可否認(rèn)的說徒河,它也有弊端:
基類需要繼承RxAppCompatActivity和RxFragment等,這也是最大的弊端送漠。這如今我們的Activity都已經(jīng)封裝好了一個(gè)完整的基類顽照,但要用RxLifeCycle,又需要換成這個(gè)闽寡,限制太大代兵。
對(duì)應(yīng)MVP框架結(jié)構(gòu)來說,無法在P層使用爷狈,只能在V層才能使用植影,不符合MVP結(jié)構(gòu)。
當(dāng)然也有更好的解決方式涎永,那就是下一篇要講的 Rxjava解除訂閱②:AutoDispose