RxJava解除订阅那些事~。

引言

RxJavaAndroid开发中的地位就不说了,稍微有点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实现解除订阅的原理,已经解释得很清楚了,如果你还是不清楚,去了解下compostakeUtil这两个操作符,相信只要明白了他们的原理和用法,以上也就能轻松的理解了。 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解除订阅的方式了,无疑最好使的还是第三种。如果你有更好的方式,也欢迎交流探讨。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值