引言
RxJava在Android开发中的地位就不说了,稍微有点RxJava使用经验的同学都知道,在Activity销毁的时候要进行解除订阅操作,不然轻则引起内存泄漏,严重一点直接导致应用程序Crash,这篇文章我们就来聊聊几种常见的解除订阅的方法。
举个例子
我们有两个Activity,第一个Activity负责启动第二个Activity,然后我们在第二个Activity进行Rxjava订阅操作。
第一个Activity:
//第一个Activity 很简单 负责启动第二个Activity
public class FirstActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_first);
findViewById(R.id.bt_launch).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//启动第二个Activity
startActivity(new Intent(FirstActivity.this,SecondActivity.class));
}
});
}
}
第二个Activity:
//第二个Activity也很简单,在里面进行RxJava订阅操作
public class SecondActivity extends AppCompatActivity {
String TAG = "SecondActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
//interval操作符,每3s发送一个从0自增的long型数据
//也就是每3s依次发送0,1,2,3,4....
Observable.interval(3, TimeUnit.SECONDS)
.subscribe(new Consumer<Long>() {
@Override
public void accept(Long aLong) throws Exception {
Log.d(TAG, aLong.toString());//打印日志
}
});
}
}
运行App,然后启动第二个Activity,观察打印日志。
04-16 22:29:15.052 32510-32636/april.lesincs.rxlifecycle_demo D/SecondActivity: 0
04-16 22:29:18.051 32510-32636/april.lesincs.rxlifecycle_demo D/SecondActivity: 1
04-16 22:29:21.051 32510-32636/april.lesincs.rxlifecycle_demo D/SecondActivity: 2
04-16 22:29:24.051 32510-32636/april.lesincs.rxlifecycle_demo D/SecondActivity: 3
04-16 22:29:27.051 32510-32636/april.lesincs.rxlifecycle_demo D/SecondActivity: 4
04-16 22:29:30.051 32510-32636/april.lesincs.rxlifecycle_demo D/SecondActivity: 5
04-16 22:29:33.051 32510-32636/april.lesincs.rxlifecycle_demo D/SecondActivity: 6
04-16 22:29:36.051 32510-32636/april.lesincs.rxlifecycle_demo D/SecondActivity: 7
可以看到,每3s会产生一个打印日志,也就是下游每3s接受到一个事件。我们不做任何解除订阅操作,然后按下back键回到第一个Activity。然后继续观察打印日志。
04-16 22:29:37.317 32510-32544/april.lesincs.rxlifecycle_demo D/AppTracker: App Event: stop
04-16 22:29:37.341 32510-32573/april.lesincs.rxlifecycle_demo D/AppTracker: App Event:start
04-16 22:29:39.051 32510-32636/april.lesincs.rxlifecycle_demo D/SecondActivity: 8
04-16 22:29:42.051 32510-32636/april.lesincs.rxlifecycle_demo D/SecondActivity: 9
04-16 22:29:45.051 32510-32636/april.lesincs.rxlifecycle_demo D/SecondActivity: 10
04-16 22:29:48.051 32510-32636/april.lesincs.rxlifecycle_demo D/SecondActivity: 11
04-16 22:29:51.051 32510-32636/april.lesincs.rxlifecycle_demo D/SecondActivity: 12
04-16 22:29:54.051 32510-32636/april.lesincs.rxlifecycle_demo D/SecondActivity: 13
04-16 22:29:57.051 32510-32636/april.lesincs.rxlifecycle_demo D/SecondActivity: 14
04-16 22:30:00.051 32510-32636/april.lesincs.rxlifecycle_demo D/SecondActivity: 15
.....
可以看到,虽然我们按了back键销毁了第二个Activity,下游还是在不断获得事件,也就是订阅还未被解除,细思极恐!这种情况就很容易造成各式各样的问题,所以我们必须在Activity销毁时解除订阅,接下来我们就说说几种解除订阅的方法。
1.常规方式解除订阅
我们都知道当订阅产生的时候,会返回一个Disposable,我们可以用RxJava提供的CompsiteDisposable容器去装下这些订阅返回的Disposable,然后在onDestroy去主动取消订阅。
也就是这样:
public class SecondActivity extends AppCompatActivity {
String TAG = "SecondActivity";
//用于存放Disposable的容器
CompositeDisposable compositeDisposable = new CompositeDisposable();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
//得到该订阅返回的Disposable
Disposable disposable = Observable.interval(3, TimeUnit.SECONDS)
.subscribe(new Consumer<Long>() {
@Override
public void accept(Long aLong) throws Exception {
Log.d(TAG, aLong.toString());
}
}
);
//将disposable添加到容器
compositeDisposable.add(disposable);
}
@Override
protected void onDestroy() {
//在onDestory中,手动解除订阅
compositeDisposable.dispose();
super.onDestroy();
}
}
经测试,经过以上步骤后,销毁Activity之后订阅已被解除。日志就不打出来了。下面说说第二种解除订阅的方式。
2.使用trello/RxLifecycle开源库
看看这个库怎么使用吧,还是上面例子,我们看看使用这个库怎么解除订阅操作。
/*首先需要继承RxAppCompatActivity*/
public class SecondActivity extends RxAppCompatActivity {
String TAG = "SecondActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
Observable.interval(3, TimeUnit.SECONDS)
.compose(this.<Long>bindUntilEvent(ActivityEvent.DESTROY)) //在订阅之前加上compose操作符并绑定结束事件为DESTORY,Activity执行onDestroy时,该订阅自动取消
.subscribe(new Consumer<Long>() {
@Override
public void accept(Long aLong) throws Exception {
Log.d(TAG, aLong.toString());
}
});
}
}
可以看到使用Rxlifecycle,结束订阅只需要继承RxAppCompatActivity,然后在订阅之前加上compose操作符并绑定结束事件为DESTORY就可以了,工作量比第一种方法要少了许多。那RxLifecycle是怎么做到呢?
首先是要理解compose这个操作符,知道他的作用。compose是一个变换操作符,但不同于map等对发射事件的变换,他是对整个事件流的变换,可以理解为,给我一个流,我对你的流进行加工,再还给你。看了下面这个例子你会容易理解。
在网络请求中,一般在io线程产生数据,然后在主线程更新数据,所以这种写法你一定见过。
Observable.just("1") //假设这是Retrofit返回的Observable
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(...)
使用compose可以简化这种写法,像这样。
Observable.just("1") //假设这是Retrofit返回的Observable
.compose(new ObservableTransformer<String, String>() {
@Override
public ObservableSource<String> apply(Observable<String> upstream) {
return upstream.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}
})
.subscribe(...);
这种写法和上面的写法实现的效果是一样的,你可能会想:你在逗我?这代码简化了?可能在上面的代码中看着反而更复杂了,不过使用lambda表达式的话,看着会更简洁一点,这里不讨论这个,只要你理解了compose操作符的用法就行了,他会拿到上游的Observable然后让你加工,最后返回加工后的Observable。
好了,我们大概有了点眉目,RxLifecycle估计就是通过对流的加工,然后达到了自动解除订阅的效果。我们看下他是怎么加的工。bindUntilEvent( ActivityEvent event)方法是RxAppCompatActivity提供的,我们看看RxAppCompatActivity的源码。
public abstract class RxAppCompatActivity extends AppCompatActivity implements LifecycleProvider<ActivityEvent> {
//BehaviorSubject这里把它当成一个可以发送数据的Observable就行了,
实际上它还可以作为Observer,有兴趣的可以去了解下
private final BehaviorSubject<ActivityEvent> lifecycleSubject = BehaviorSubject.create();
@Override
@NonNull
@CheckResult
//这个方法本文没有使用到,但是我还是说下我的见解
public final Observable<ActivityEvent> lifecycle() {
//上面说到BehaviorSubject既可以作为Observable也可以作为Observer,
也就是既可以是观察者,也可以是被观察者,
hide()这个方法名很贴切,我们可以看到返回的是一个Observable,
实际上hide可以理解为隐藏自己的观察者身份,纯碎作为被观察者
return lifecycleSubject.hide();
}
@Override
@NonNull
@CheckResult
public final <T> LifecycleTransformer<T> bindUntilEvent(@NonNull ActivityEvent event) {
//这里调用了RxLifecycle的静态方法,然后传入了我们的上面的BehaviorSubject以及event
return RxLifecycle.bindUntilEvent(lifecycleSubject, event);
}
@Override
@NonNull
@CheckResult
//这个是RxLifecycle其他的一些用法,本文用不到
public final <T> LifecycleTransformer<T> bindToLifecycle() {
return RxLifecycleAndroid.bindActivity(lifecycleSubject);
}
@Override
@CallSuper
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//在OnCreate中发射ActivityEvent.CREATE事件
lifecycleSubject.onNext(ActivityEvent.CREATE);
}
@Override
@CallSuper
protected void onStart() {
//在onStart中发射ActivityEvent.START事件,下面同理
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的源码,我们可以大致总结它做了什么工作:在每一次生命周期回调的时候使用成员lifecycleSubject(理解为一个Observable)发射一个对应的事件,我们接着看在compose中调用的bindUntilEvent(ActivityEvent event)方法。
//可以看到,调用了RxLifecycle的静态bindUntilEvent,并传入了lifecycleSubject和event对象
public final <T> LifecycleTransformer<T> bindUntilEvent(@NonNull ActivityEvent event) {
return RxLifecycle.bindUntilEvent(lifecycleSubject, event);
}
我们看下静态方法长啥样子。
public static <T, R> LifecycleTransformer<T> bindUntilEvent(@Nonnull final Observable<R> lifecycle,
@Nonnull final R event) {
checkNotNull(lifecycle, "lifecycle == null");
checkNotNull(event, "event == null");
//检查了空指针然后调用bind方法,takeUntilEvent返回的Observable被作为参数传入bind方法。
return bind(takeUntilEvent(lifecycle, event));
}
public static <T, R> LifecycleTransformer<T> bind(@Nonnull final Observable<R> lifecycle) {
1.返回一个LifecycleTransformer,构造函数接受一个Observable,即takeUntilEvent返回的Observable
2.我们知道compose操作符接受一个ObservableTransformer接口对象,不用想LifecycleTransformer肯定实现了这个接口
return new LifecycleTransformer<>(lifecycle);
}
private static <R> Observable<R> takeUntilEvent(final Observable<R> lifecycle, final R event) {
//重点来了,这里的lifecycle变量是我们前面提到的lifecycleSubject,
在RxAppCompatActivity源码中我们知道他在Activity每次生命周期回调的时候发射相应的生命事件,
这里又把他进行了一次封装成另一个Observable,
即当生命事件等于我们传入的事件时,才能发射数据!
即当生命事件等于我们传入的事件时,才能发射数据!
即当生命事件等于我们传入的事件时,才能发射数据!
return lifecycle.filter(new Predicate<R>() {
@Override
public boolean test(R lifecycleEvent) throws Exception {
return lifecycleEvent.equals(event);
}
});
}
我们看下LifecycleTransformer这个类,这里我们只关心ObservableSource<Downstream> apply(Observable<Upstream> upstream)这个方法,因为这个方法是ObservableTransformer接口中唯一的方法,我们可以在这里的参数拿到上游的Observable,即upStream。
final Observable<?> observable;
LifecycleTransformer(Observable<?> observable) {
checkNotNull(observable, "observable == null");
//保存了我们上面传进来的Observable到成员变量
this.observable = observable;
}
@Override
public ObservableSource<T> apply(Observable<T> upstream) {
1.仅仅是对上游使用了takeUtil操作符,并传入了我们构造函数传入的Observable
2.解释下takeUtil操作符,takeUtil操作符接受一个Observable,当这个Observable发送一个任意数据时,tailUtil链上的流断开,(即取消订阅)
3.至此,形式已经明朗,我们知道我们传入的Observable最开始是BehaviorSubject,
并且在Activity所有生命周期回调的时候发射相应的事件,但是我们在传入的过程中对其进行了一次加工,
即生命周期事件与传入的事件对应时,才发射数据。比如:当我们传入事件为ActivityEvent.DESTROY时,在Activity执行OnDestroy时,此时的observable才会发送事件,造成takeUtil的链断开。
return upstream.takeUntil(observable);
}
以上就是RxLifecycle实现解除订阅的原理,已经解释得很清楚了,如果你还是不清楚,去了解下compos和takeUtil这两个操作符,相信只要明白了他们的原理和用法,以上也就能轻松的理解了。
RxLifecycle已经足够好用,但是这还不够,我们讨厌为了实现一个功能而去继承一个类,这样对代码的侵入太大了。因此,知乎团队同名的RxLifecycle开源库应运而生。
3.使用zhihu/RxLifecycle同名开源框架解除订阅。
用法,还是上面例子:
/*无需继承额外的扩展Activity*/
public class SecondActivity extends AppCompatActivity {
String TAG = "SecondActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
io.reactivex.Observable.interval(3, TimeUnit.SECONDS)
.compose(RxLifecycleCompact.bind(this).<Long>disposeObservableWhen(LifecycleEvent.DESTROY)) //这里类似于第二种,只是API不同
.subscribe(new Consumer<Long>() {
@Override
public void accept(Long aLong) throws Exception {
Log.d(TAG,aLong.toString());
}
});
}
}
可以看到,知乎团队出的同名RxLifecycle相比第二种方案更加粗暴,甚至不用继承额外的Activity,代码侵入性更小。两者主要实现原理一样,只是知乎的这个把监听生命周期事件放在了一个无UI的fragment中,这个套路也很常见了,这里不再赘述,感兴趣的可以去看看源码。
最后
以上就是三种RxJava解除订阅的方式了,无疑最好使的还是第三种。如果你有更好的方式,也欢迎交流探讨。