一、介紹
RxLifecycle
目的:解決RxJava
使用中的記憶體洩漏問題。
例如,當使用RxJava
訂閱並執行耗時任務後,當Activity
被finish
時,如果耗時任務還未完成,沒有及時取消訂閱,就會導致Activity
無法被回收,從而引發記憶體洩漏。
為了解決這個問題,就產生了RxLifecycle
,讓RxJava
變得有生命週期感知,使得其能及時取消訂閱,避免出現記憶體洩漏問題。
二、使用
首先來介紹下RxLifecycle
的使用。
1.新增依賴
implementation 'com.trello.rxlifecycle2:rxlifecycle:2.2.1'
implementation 'com.trello.rxlifecycle2:rxlifecycle-android:2.2.1'
implementation 'com.trello.rxlifecycle2:rxlifecycle-components:2.2.1'
複製程式碼
2.繼承容器類
Activity/Fragment
需要繼承RxAppCompatActivity/RxFragment
,主要支援如下幾種容器類:
Rx
類即可,這一步主要是對生命週期的回撥事件進行監聽。
3.繫結容器生命週期
以Activity
為例,主要有如下兩種方法:
bindUntilEvent(@NonNull ActivityEvent event)
複製程式碼
bindToLifecycle()
複製程式碼
針對Fragment
也有同樣的兩種方法,只是方法名會有所不同。
下面詳細介紹這兩種方法的區別:
bindUntilEvent
該方法指定在哪個生命週期方法呼叫時取消訂閱。
其中ActivityEvent
是一個列舉類,對應於Activity
的生命週期。
public enum ActivityEvent {
CREATE,
START,
RESUME,
PAUSE,
STOP,
DESTROY
}
複製程式碼
具體使用示例:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
Observable.interval(1, TimeUnit.SECONDS)
.doOnDispose {
Log.i(TAG, "Unsubscribing subscription from onDestory()")
}
.compose(bindUntilEvent(ActivityEvent.DESTROY))
.subscribe {
Log.i(TAG, "Started in onCreate(), running until in onDestroy(): $it")
}
}
複製程式碼
指定在生命週期onDestory()
時,取消訂閱。
bindToLifecycle
在某個生命週期進行繫結,在對應的生命週期進行訂閱解除。
具體使用示例:
override fun onResume() {
super.onResume()
Observable.interval(1, TimeUnit.SECONDS)
.doOnDispose {
Log.i(TAG, "Unsubscribing subscription from onPause()")
}
.compose(bindToLifecycle())
.subscribe {
Log.i(TAG, "Started in onResume(), running until in onPause(): $it")
}
}
複製程式碼
在onResume()
進行繫結訂閱,則在onPause()
進行解除訂閱,生命週期是兩兩對應的。
三、原理解析
1.compose
首先來了解一下compose
操作符。
compose(bindToLifecycle())
compose(bindUntilEvent(ActivityEvent.DESTROY))
複製程式碼
如上所示,兩種繫結生命週期的方式,都是通過compose
操作符進行實現的。
compose
一般情況下可以配合Transformer
使用,以實現將一種型別的Observable
轉換成另一種型別的Observable
,保證呼叫的鏈式結構。
那麼接下來看該操作符在RxLifecycle
中的應用,從bindToLifecycle
和bindUntilEvent
入手。
2.BehaviorSubject
public abstract class RxAppCompatActivity extends AppCompatActivity implements LifecycleProvider<ActivityEvent> {
private final BehaviorSubject<ActivityEvent> lifecycleSubject = BehaviorSubject.create();
@Override
@NonNull
@CheckResult
public final Observable<ActivityEvent> lifecycle() {
return lifecycleSubject.hide();
}
@Override
@NonNull
@CheckResult
public final <T> LifecycleTransformer<T> bindUntilEvent(@NonNull ActivityEvent event) {
return RxLifecycle.bindUntilEvent(lifecycleSubject, event);
}
@Override
@NonNull
@CheckResult
public final <T> LifecycleTransformer<T> bindToLifecycle() {
return RxLifecycleAndroid.bindActivity(lifecycleSubject);
}
@Override
@CallSuper
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
lifecycleSubject.onNext(ActivityEvent.CREATE);
}
@Override
@CallSuper
protected void onStart() {
super.onStart();
lifecycleSubject.onNext(ActivityEvent.START);
}
@Override
@CallSuper
protected void onResume() {
super.onResume();
lifecycleSubject.onNext(ActivityEvent.RESUME);
}
@Override
@CallSuper
protected void onPause() {
lifecycleSubject.onNext(ActivityEvent.PAUSE);
super.onPause();
}
@Override
@CallSuper
protected void onStop() {
lifecycleSubject.onNext(ActivityEvent.STOP);
super.onStop();
}
@Override
@CallSuper
protected void onDestroy() {
lifecycleSubject.onNext(ActivityEvent.DESTROY);
super.onDestroy();
}
}
複製程式碼
RxAppCompatActivity
中有一個關鍵物件BehaviorSubject
BehaviorSubject
會傳送離訂閱最近的上一個值,沒有上一個值的時候會傳送預設值。如下圖:
所以lifecycleSubject
會根據繫結訂閱的時期,不斷髮送接下來的生命週期事件ActivityEvent
。
3.LifecycleTransformer
接下來繼續看原始碼,bindToLifecycle
和bindUntilEvent
都返回了一個LifecycleTransformer
物件,那麼LifecycleTransformer
到底有什麼用?
@ParametersAreNonnullByDefault
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));
}
@Override
public boolean equals(Object o) {
if (this == o) { return true; }
if (o == null || getClass() != o.getClass()) { return false; }
LifecycleTransformer<?> that = (LifecycleTransformer<?>) o;
return observable.equals(that.observable);
}
@Override
public int hashCode() {
return observable.hashCode();
}
@Override
public String toString() {
return "LifecycleTransformer{" +
"observable=" + observable +
'}';
}
}
複製程式碼
LifecycleTransformer
實現了各種Transformer
介面,能夠將一個 Observable/Flowable/Single/Completable/Maybe
物件轉換成另一個 Observable/Flowable/Single/Completable/Maybe
物件。正好配合上文的compose
操作符,使用在鏈式呼叫中。
4.takeUntil
接下來到了關鍵了,LifecycleTransformer
到底把原來的Observable
物件轉換成了什麼樣子?
這就需要了解takeUntil
操作符了!
Observable
發射了一項資料或者終止時,丟棄原Observable
發射的任何資料。所謂的第二個Observable
,即傳入takeUntil
中的Observable
物件。
理解了該操作符的作用,那麼你可能就明白了,RxLifecycle
就是通過監聽第二個Observable
發射的資料,來解除訂閱。
那麼這第二個Observable
是誰?
不就是在建立LifecycleTransformer
的時候傳入建構函式中的嘛,那就來尋找一下什麼時候建立的該物件即可。
從頭開始捋一捋:
public final <T> LifecycleTransformer<T> bindUntilEvent(@NonNull ActivityEvent event) {
return RxLifecycle.bindUntilEvent(lifecycleSubject, event);
}
複製程式碼
該方法返回了LifecycleTransformer
物件,繼續向下追溯。
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);
}
});
}
複製程式碼
繼續追蹤,馬上接近真相。
public static <T, R> LifecycleTransformer<T> bind(@Nonnull final Observable<R> lifecycle) {
return new LifecycleTransformer<>(lifecycle);
}
複製程式碼
在該方法中建立了該物件,並傳入了一個Observable
物件,通過上面方法即可知道該物件就是BehaviorSubject
物件。
那麼該物件在什麼時候傳送第一次資料呢?
這就要看上面的takeUntilEvent
方法了。
關鍵在這一句lifecycleEvent.equals(event)
,只有當BehaviorSubject
傳送的ActivityEvent
的值等於解除繫結的生命週期時,才會傳送第一次資料。那麼當傳送第一次資料時,根據上面的分析就會解除訂閱的繫結。
那麼針對bindToLifecycle
方法,是進行怎樣的操作,使得在對應的生命週期進行解除訂閱呢?
還是繼續看原始碼。
public final <T> LifecycleTransformer<T> bindToLifecycle() {
return RxLifecycleAndroid.bindActivity(lifecycleSubject);
}
複製程式碼
public static <T> LifecycleTransformer<T> bindActivity(@NonNull final Observable<ActivityEvent> lifecycle) {
return bind(lifecycle, ACTIVITY_LIFECYCLE);
}
複製程式碼
其中ACTIVITY_LIFECYCLE
為:
private static final Function<ActivityEvent, ActivityEvent> ACTIVITY_LIFECYCLE =
new Function<ActivityEvent, ActivityEvent>() {
@Override
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");
}
}
};
複製程式碼
該函式的功能是會根據傳入的生命週期事件,返回對應的生命週期,如CREATE
→DESTROY
。看來通過該函式就可以實現在對應生命週期解綁了。
不過還需要一系列操作符的協助,繼續看原始碼。
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);
}
複製程式碼
詳細看一下takeUntilCorrespondingEvent
方法。
5.take
首先看一下take
操作符,很簡單。
take(int)
用一個整數n作為一個引數,只發射前面的n項,如下圖:
那麼對應lifecycle.take(1).map(correspondingEvents)
,即獲取傳送的第一個生命週期事件,再通過上面對應的函式,轉換為響應的生命週期。如果在onCreate
中進行繫結,那麼第一個傳送的就是CREATE
,返回的就是對應的DESTORY
。
6.skip
skip(int)
忽略Observable
發射的前n項資料
lifecycle.skip(1)
,如果在onCreate
中進行繫結,那麼剩餘的就是START
,RESUME
,PAUSE
,STOP
,DESTROY
7. combineLatest
最後還需要一個關鍵的操作符combineLatest
,來完成對應生命週期的解除訂閱。
combineLatest
操作符可以將2~9個Observable
發射的資料組裝起來然後再發射出來。不過還有兩個前提:
- 所有的Observable都發射過資料。
- 滿足上面條件的時候任何一個
Observable
發射一個資料,就將所有Observable
最新發射的資料按照提供的函式組裝起來發射出去。
具體示例,如下圖所示:
按照第三個引數的函式,將lifecycle.take(1).map(correspondingEvents)
和lifecycle.skip(1)
,進行combine
new BiFunction<R, R, Boolean>() {
@Override
public Boolean apply(R bindUntilEvent, R lifecycleEvent) throws Exception {
return lifecycleEvent.equals(bindUntilEvent);
}
}
複製程式碼
那麼結果是
false,false,false,false,true
複製程式碼
之後的onErrorReturn
和filter
是對異常的處理和判斷是否應該結束訂閱:
//異常處理
static final Function<Throwable, Boolean> RESUME_FUNCTION = new Function<Throwable, Boolean>() {
@Override
public Boolean apply(Throwable throwable) throws Exception {
if (throwable instanceof OutsideLifecycleException) {
return true;
}
//noinspection ThrowableResultOfMethodCallIgnored
Exceptions.propagate(throwable);
return false;
}
};
//是否應該取消訂閱,依賴於上游的boolean
static final Predicate<Boolean> SHOULD_COMPLETE = new Predicate<Boolean>() {
@Override
public boolean test(Boolean shouldComplete) throws Exception {
return shouldComplete;
}
};
複製程式碼
所以,按照上面的例子,如果在onCreate()
方法中進行繫結,那麼在onDestory()
方法中就會對應的解除訂閱。
四、總結
通過上面的分析,可以瞭解RxLifecycle
的使用以及原理。
學習RxLifecycle
的過程中,更加體會到了對於觀察者模式的使用,以及RxJava
操作符的強大,各種操作符幫我們實現一些列的轉換。