使用RxJava引起的内存泄漏
RxJava很好用,但是随着订阅的增多内存开销也会随之增大,尤其是在配合使用网络请求的时候,当页面被finish,此时订阅逻辑还未完成,如果没有及时取消订阅,就会导致Activity/Fragment无法被回收,从而引发内存泄漏。
当我们不需要的时候,主动取消订阅。比如在下面的代码中,我们开启一个周期任务用来不断的输出信息,那么我们需要在该Activity被销毁的时候调用mSubscription.unsubscribe()来主动的解除订阅关系防止内存泄漏。
@Override
protected void onDestroy() {
super.onDestroy();
//主动解除订阅关系
if (mSubscription != null && !mSubscription.isUnsubscribed()) {
mSubscription.unsubscribe();
}
}
看完上面简单的示例,想必你也明白rxjava所造成的内存泄漏往往和组件的生命周期相关。也就是我们要重点关注那些在在组件销毁之后,订阅关系却仍然存在的情况。大部分情况下,当我们的视图销毁之后,订阅关系就没有必要存在了,所以需要我们主动取消订阅即可。
在我们的工程中,往往存在很多个视图(Activity,Fragment等),如果在每个视图当中都要手动的解除订阅关系是件很繁琐的事情。这里有两种方式:一是在基类当中,比如BaseActivity,BaseFragment中统一取消订阅,另外一种方式就是使用RxLifeCycle这个库。
RxLifecycle
RxLifeCycle主要提供了两个方法bindToLifecycle()和bindUntilEvent(),分别用来绑定生命周期和事件。
bindToLifecycle( )
绑定生命周期的做法本质上是通过监听组件(Activity,Fragment)生命周期的变化来自动解除订阅关系。用法如下:
// Using automatic unsubscription, this should determine that the correct time to
// unsubscribe is onStop (the opposite of onStart).
Observable.interval(1, TimeUnit.SECONDS)
.doOnDispose(new Action() {
@Override
public void run() throws Exception {
Log.i(TAG, "Unsubscribing subscription from onStart()");
}
})
.compose(this.<Long>bindToLifecycle())
.subscribe(new Consumer<Long>() {
@Override
public void accept(Long num) throws Exception {
Log.i(TAG, "Started in onStart(), running until in onStop(): " + num);
}
});
- 在onResume中绑定,将在对应的onPause()方法中解除订阅关系
- 在onStart()中绑定,将在对应的onStop()中解除订阅关系
- 在onCreate()中绑定,将在对应的onDestory()中解除订阅关系
bindUntilEvent()
和使用bindToLifecycle()不一样的是,绑定事件的方式只关心何时解除订阅关系。因为在很多情况下,我们所做的操作并不一定是在onResume()开始,在onPause()结束,此时显然不能用绑定生命周期的方法。来看看绑定事件如何使用:
// `this.<Long>` is necessary if you're compiling on JDK7 or below.
// If you're using JDK8+, then you can safely remove it.
Observable.interval(1, TimeUnit.SECONDS)
.doOnDispose(new Action() {
@Override
public void run() throws Exception {
Log.i(TAG, "Unsubscribing subscription from onResume()");
}
})
.compose(this.<Long>bindUntilEvent(ActivityEvent.DESTROY))
.subscribe(new Consumer<Long>() {
@Override
public void accept(Long num) throws Exception {
Log.i(TAG, "Started in onResume(), running until in onDestroy(): " + num);
}
});
注:如果你使用的是JDK7或更低版本,要在bindUntilEvent()方法前加this.。
从bindUntilEvent(ActivityEvent.DESTROY);开始
public abstract class RxAppCompatActivity extends AppCompatActivity implements LifecycleProvider<ActivityEvent> {
private final BehaviorSubject<ActivityEvent> lifecycleSubject = BehaviorSubject.create();
@Override
@NonNull
@CheckResult
public final <T> LifecycleTransformer<T> bindUntilEvent(@NonNull ActivityEvent event) {
return RxLifecycle.bindUntilEvent(lifecycleSubject, event);
}
//省略其他代码
}
一、BehaviorSubject是什么?
点开 RxJava2文档 ,找到关于Subject的描述:
public abstract class Subject<T>
extends Observable<T>
implements Observer<T>
对于Observable,Observer,Subscriber我们比较熟悉,故不做说明,重点来看Subject。
通过上面的图我们可以看出Subject继承自Observable,也就意味着Subject可以作为被观察者,另外,它又实现了Observer接口,这意味着它也可以作为观察者。不难看出,Subject既能作为Observer订阅Observable,又能作为Observable被其他Observer订阅。总之,Subject承担了这么一种角色:对上作为观察者,对下作为被观察者。
二、常见的Subject
从上面的uml中我们看出,RxJava为我们提供了四种常用的Subject,即
AsyncSubject,BehabviorSubject,PublishSubject,ReplaySubject,下面我们对这四者进行说明。
1.AsyncSubject
An Subject that emits the very last value followed by a completion event or the received error to Observers.
AsyncSubject会缓存最后一个数据并在调用onCompleted()时将该数据发送给订阅者,在该过程中,一旦发生任何异常都不会发送数据到订阅者,而是发送给订阅者一个异常通知,即订阅者只能接受到一个异常的通知。
举例来说明AsyncSubject的用法:
asyncSubject.onNext("1");
asyncSubject.onNext("2");
asyncSubject.onCompleted();//必须调用才会开始发送数据
以上代码执行后,订阅者接受到的数据是2.
2.BehabviorSubject
Subject that emits the most recent item it has observed and all subsequent observed items to each subscribed Observer.
当BehaviorSubject被订阅后,它首先会发送原始Observable最近发射的数据,如果最近没有,会发射一个默认值,接下继续发射原始Observable的数据。
例子如下:
// observer will receive all 4 events (including "default").
BehaviorSubject<Object> subject = BehaviorSubject.createDefault("default");
subject.subscribe(observer);
subject.onNext("one");
subject.onNext("two");
subject.onNext("three");
// observer will receive the "one", "two" and "three" events, but not "zero"
BehaviorSubject<Object> subject = BehaviorSubject.create();
subject.onNext("zero");
subject.onNext("one");
subject.subscribe(observer);
subject.onNext("two");
subject.onNext("three");
// observer will receive only onComplete
BehaviorSubject<Object> subject = BehaviorSubject.create();
subject.onNext("zero");
subject.onNext("one");
subject.onComplete();
subject.subscribe(observer);
// observer will receive only onError
BehaviorSubject<Object> subject = BehaviorSubject.create();
subject.onNext("zero");
subject.onNext("one");
subject.onError(new RuntimeException("error"));
subject.subscribe(observer);
3.PublishSubject
Subject that, once an Observer has subscribed, emits all subsequently observed items to the subscriber.
Observable一旦被订阅就开始发送事件,如下图所示:
例子:
PublishSubject<Object> subject = PublishSubject.create();
// observer1 will receive all onNext and onComplete events
subject.subscribe(observer1);
subject.onNext("one");
subject.onNext("two");
// observer2 will only receive "three" and onComplete
subject.subscribe(observer2);
subject.onNext("three");
subject.onComplete();
4.ReplaySubject
Replays events to Observers.
ReplaySubject会缓存所有已经发射的数据,当一个新的订阅关系产生时,ReplaySuject会将所有数据都发送给他。如下图:
ReplaySubject<Object> subject = new ReplaySubject<>();
subject.onNext("one");
subject.onNext("two");
subject.onNext("three");
subject.onComplete();
// both of the following will get the onNext/onComplete calls from above
subject.subscribe(observer1);
subject.subscribe(observer2);
Subject | |
---|---|
AsyncSubject | 不论订阅发生在什么时候,只会发射最后一个数据 |
BehaviorSubject | 发送订阅之前一个数据和订阅之后的全部数据 |
ReplaySubject | 不论订阅发生在什么时候,都发射全部数据 |
PublishSubject | 发送订阅之后全部数据 |
三、RxLifecycle.bindUntilEvent()
我们回过头来重新看之前的完整的源码:
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();
}
}
可以看到RxLifecycle.bindUntilEvent()这个函数的返回值类型是LifecycleTransformer,这个类实现了ObservableTransformer接口,主要功能如下:
@Override
public ObservableSource<T> apply(Observable<T> upstream) {
return upstream.takeUntil(observable);
}
TakeUtil操作符
takeUntil订阅原始的Observable并发射数据,此外它还监视你提供的第二个Observable。当第二个Observable发射了一项数据或者发射一项终止的通知时(onError通知或者onCompleted通知),TakeUntil返回的Observable会停止发射原始的Observable,如下图所示:
到现在为止,我们已经知道了RxLifeCycle利用BehaviorSubject来监视生命周期的变化,用takeUtil操作符让原有的Observable(比如网络请求的Observable)来监视Subject发射的数据呢,并根据Subject的状态自动停止原始数据的发射。
跟进bindUntilEvent()方法:
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);
}
});
}
通过filter操作符进行过滤,只有绑定的事件与BehaviorSubject在AppCompatActivity各个生命周期发射的事件相同的时候,才提交给订阅者,让他自动停止原始数据的发射。
至此,这个开源生命周期管理框架分析结束。
谢谢。