Rx系列<第九篇>:RxJava之Cold Observable 和 Hot Observable

Observable 数据流有两种类型:hot 和 cold。

(1)Cold Observable

只有当有订阅者订阅的时候, Cold Observable 才开始执行发射数据流的代码。并且每个订阅者订阅的时候都独立的执行一遍数据流代码。 Observable.interval就是一个 典型的Cold Observable。每一个订阅者都会独立的收到他们的数据流。

我们来验证一下:

    Observable observable =  Observable.interval(1000, TimeUnit.MILLISECONDS);
    observable.subscribe(new Consumer() {
        @Override
        public void accept(Long l) throws Exception {
            Log.d("aaa", "第一个:"+String.valueOf(l));
        }
    });
    observable.subscribe(new Consumer() {
        @Override
        public void accept(Long l) throws Exception {
            Log.d("aaa", "第二个:"+String.valueOf(l));
        }
    });

运行效果

图片.png

查看运行效果可知每个订阅者订阅的时候都独立的执行一遍数据流代码

现在我们换一种写法:

    Observable observable =  Observable.create(new ObservableOnSubscribe() {

        @Override
        public void subscribe(ObservableEmitter emitter) throws Exception {
            emitter.onNext(1);
            emitter.onNext(2);
            emitter.onNext(3);
        }
    });
    observable.subscribe(new Consumer() {
        @Override
        public void accept(Long l) throws Exception {
            Log.d("aaa", "第一个:"+String.valueOf(l));
        }
    });
    observable.subscribe(new Consumer() {
        @Override
        public void accept(Long l) throws Exception {
            Log.d("aaa", "第二个:"+String.valueOf(l));
        }
    });

这是我们最常用的写法,显然只有当只有开始在订阅的时候才会执行subscribe回调方法,然后发射数据emitter.onNext的操作是在subscribe回调方法里执行的,也就是说只有在订阅的时候才会发射数据

除了intervalcreate属于Cold Observable之外,还有justrangetimerfrom也属于Cold Observable。

(2)Hot Observable

怎么区分是Cold Observable还是Hot Observable?
(1) Cold Observable只有在订阅的时候才会发射数据,Hot Observable在订阅之前就可以发射数据;
(2) Cold Observable的数据流都是独立,而Hot Observable的数据流是共享的;

  • 使用publish关键字(将Observable转成ConnectableObservable)

Cold Observable 和 Hot Observable可以相互转换,使用publish关键字
可以将Cold Observable转成Hot Observable。

    ConnectableObservable observable =  Observable.create(new ObservableOnSubscribe() {
        @Override
        public void subscribe(ObservableEmitter emitter) {
            CountBean countBean = new CountBean();
            Log.d("aaa", "开始发射数据");
            emitter.onNext(countBean.hashCode());
            Log.d("aaa", "发射数据完成");

        }
    }).observeOn(Schedulers.newThread()).publish();

    observable.connect();//必须添加

以上代码没有订阅,打印日志如下:

图片.png

也就是说Hot Observable数据发射的时机是执行connect()之后。

下面修改代码,添加订阅代码

    ConnectableObservable observable =  Observable.create(new ObservableOnSubscribe() {
        @Override
        public void subscribe(ObservableEmitter emitter) {
            CountBean countBean = new CountBean();
            Log.d("aaa", "开始发射数据");
            emitter.onNext(countBean);
            Log.d("aaa", "发射数据完成:"+countBean.hashCode());

        }
    }).publish();

    observable.subscribe(new Consumer() {
        @Override
        public void accept(CountBean countBean) throws Exception {
            Log.d("aaa", "第一个:"+String.valueOf(countBean.hashCode()));
        }
    });

    observable.subscribe(new Consumer() {
        @Override
        public void accept(CountBean countBean) throws Exception {
            Log.d("aaa", "第二个:"+String.valueOf(countBean.hashCode()));
        }
    });

    observable.connect();//必须添加

日志如下:

图片.png

OK,我们看到CountBean的hashcode是一样的,所以可以证明Hot Observable的数据流是共享的

  • 使用publish和refCount关键字(将ConnectableObservable转成Observable)
    Observable observable =  Observable.create(new ObservableOnSubscribe() {
        @Override
        public void subscribe(ObservableEmitter emitter) {
            CountBean countBean = new CountBean();
            Log.d("aaa", "开始发射数据");
            emitter.onNext(countBean);
            Log.d("aaa", "发射数据完成:"+countBean.hashCode());

        }
    }).publish().refCount(2);

    observable
            .observeOn(Schedulers.newThread())
            .subscribe(new Consumer() {
        @Override
        public void accept(CountBean countBean) throws Exception {
            Log.d("aaa", "第一个:"+String.valueOf(countBean.hashCode()));
        }
    });

    observable
            .observeOn(Schedulers.newThread())
            .subscribe(new Consumer() {
        @Override
        public void accept(CountBean countBean) throws Exception {
            Log.d("aaa", "第二个:"+String.valueOf(countBean.hashCode()));
        }
    });

比第一种方式简化了很多,refcount本质上在后台维护着一个引用计数器。

图片.png

refCount可以设置一些参数,可以设置最大订阅数量,也可以设置某时间范围内可订阅。

日志如下:

图片.png

利用refCount修饰的ConnectableObservable符合Hot Observable共享同一条数据的特性。

  • 使用share关键字

      Observable observable =  Observable.create(new ObservableOnSubscribe() {
          @Override
          public void subscribe(ObservableEmitter emitter) {
              CountBean countBean = new CountBean();
              Log.d("aaa", "开始发射数据");
              emitter.onNext(countBean);
              Log.d("aaa", "发射数据完成:"+countBean.hashCode());
    
          }
      }).share();
    
      Disposable disposable = observable
              .observeOn(Schedulers.newThread())
              .subscribe(new Consumer() {
          @Override
          public void accept(CountBean countBean) throws Exception {
              Log.d("aaa", "第一个:"+String.valueOf(countBean.hashCode()));
          }
      });
    
      try {
          Thread.sleep(1000);
      } catch (InterruptedException e) {
          e.printStackTrace();
      }
    
      disposable.dispose();
    
      observable
              .observeOn(Schedulers.newThread())
              .subscribe(new Consumer() {
          @Override
          public void accept(CountBean countBean) throws Exception {
              Log.d("aaa", "第二个:"+String.valueOf(countBean.hashCode()));
          }
      });
    

日志如下:

图片.png

看一下share的源码得知,其实share就是publish().refCount()。

@CheckReturnValue
@SchedulerSupport(SchedulerSupport.NONE)
public final Observable share() {
    return publish().refCount();
}

refCount()如果不传递参数,则默认只能订阅一个观察者,只有在取消订阅之后才可以继续订阅。

  • 使用Subject关键字

      Observable observable =  Observable.create(new ObservableOnSubscribe() {
          @Override
          public void subscribe(ObservableEmitter emitter) {
              CountBean countBean = new CountBean();
              Log.d("aaa", "开始发射数据");
              emitter.onNext(countBean);
              Log.d("aaa", "发射数据完成:"+countBean.hashCode());
    
          }
      });
    
      PublishSubject subject = PublishSubject.create();
    
    
      subject.subscribe(new Consumer() {
          @Override
          public void accept(CountBean countBean) throws Exception {
              Log.d("aaa", "第一个:"+String.valueOf(countBean.hashCode()));
          }
      });
    
      subject.subscribe(new Consumer() {
          @Override
          public void accept(CountBean countBean) throws Exception {
              Log.d("aaa", "第二个:"+String.valueOf(countBean.hashCode()));
          }
      });
    
      observable.subscribe(subject);
    

日志如下:

图片.png

PublishSubject的父类是Subject, 查看Subject的源码可知,Subject继承了Observable,同时又实现了Observer,也就是说,Subject同时兼备了观察者和被观察者的特性,了解这个特性之后再去理解以上代码就容易多了。

总结:

Cold Observable有个很重要的特性:每个订阅者订阅的时候都独立的执行一遍数据流代码
Hot Observable也有一个很重要的特性:每个订阅者订阅的时候都共享的执行一遍数据流代码,并且它是线程安全的。

你可能感兴趣的:(Rx系列<第九篇>:RxJava之Cold Observable 和 Hot Observable)