美文网首页Rxjava相关我的Android之路Rxjava
【译】使用RxJava实现延迟订阅

【译】使用RxJava实现延迟订阅

作者: 小鄧子 | 来源:发表于2015-08-20 22:41 被阅读16823次

    我越来越喜欢把RxJava的defer()操作符作为一个工具来使用,以确保Observable代码在被订阅后才执行(而不是创建后立即执行)。我之前写过一些有关defer()的代码,但是,现在我想做更详细的描述。

    假设,有个数据类:

    public class SomeType {  
    private String value;
    
    public void setValue(String value) {
      this.value = value;
    }
    
    public Observable<String> valueObservable() {
      return Observable.just(value);
    }
    }
    

    这段代码在运行后会打印出什么呢?

    SomeType instance = new SomeType();  
    Observable<String> value = instance.valueObservable();  
    instance.setValue("Some Value");  
    value.subscribe(System.out::println);  
    

    如果你认为会打印出“Some Value”,那就错了。而实际打印结果是“null”。因为在调用Observable.just()的时候,value已经初始化了。

    just()from()这类能够创建Observable的操作符(译者注:创建Observable的操作符)在创建之初,就已经存储了对象的值,而不被订阅的时候。这种情况,显然不是预期表现,我想要的valueObservable()是无论什么时候请求,都能够表现为当前值。

    自助

    一个解决办法就是使用Observable.create(),因为它允许为每个订阅者精确控制事件的发送。

    public Observable<String> valueObservable() {
      return Observable.create(new Observable.OnSubscribe<String>() {
        @Override public void call(Subscriber<? super String> subscriber) {
    
          subscriber.onNext(value);
          subscriber.onCompleted();
        }
      });
    }
    

    现在,valueObservable()将在订阅的时候发送当前值(事件)。它除了在订阅的时候才获取value(而不是创建的时候)之外,看起来和Observable.just()所做的没什么两样。

    现在唯一的问题是,自从阅读Dávid Karnok的解读操作符系列文章后(译者注:简直不能更优秀,一定要看),我一直小心翼翼的编写着自定义的操作符(译者注:原著的意思是指,自定义操作符内部处理方式,如上面代码中的subscriber.onNext(value)等)。通过阅读该系列,我发现很难写出正确的操作符。来看看这篇文章Observable.just()为了支持背压(译者注:例如Observable.zip()操作符)和退订是如何做出改变的。

    当然,上面那段代码是能正确运行的,至少现在看来它是OK哒,但是随着RxJava版本的不断迭代,鬼知道以后能不能。而且我也不知道类似背压和退订等操作能否安全的向下兼容。更何况,我又不是操作符开发专家。所以,我试着避免自定义操作符,除非万不得已。

    简单粗暴

    这里有一种不需要自定义操作符的实现方式:

    public Observable<String> valueObservable() {
      return Observable.defer(new Func0<Observable<String>>() {
        @Override public Observable<String> call() {
          return Observable.just(value);
        }
      });
    }
    

    我所做的就是用defer()操作符封装原始代码,但现在的表现正是我想要的。defer()中的代码直到被订阅才会执行。我们只需要在请求数据的时候调用Observable.just()就哦了。

    我更喜欢这个解决方案的原因:

    1. Observable.create()更简单,不再需要手动调用onCompleted()

    2. 使用内置操作符,这种方式(可能)更得到官方的肯定。

    使用defer()操作符的唯一缺点就是,每次订阅都会创建一个新的Observable对象。create()操作符则为每一个订阅者都使用同一个函数,所以,后者效率更高。一如既往地,如果有必要可以亲测性能或者尝试优化。

    深入

    上面代码仅仅是为讲解所用,但是,切换到实际生产中,我们需要用BehaviorSubject替换所有代码。让我们来看一些更复杂的东西。

    假设需要一个方法,首先将数据写进磁盘,然后再作为结果返回。这是一种用defer()操作符的实现:

    public Observable<SomeType> createSomeType(final String value) {
      return Observable.defer(new Func0<Observable<SomeType>>() {
        @Override public Observable<SomeType> call() {
    
          SomeType someType = new SomeType();
          someType.setValue(value);
    
          try {
            db.writeToDisk(someType);
          } catch (IOException e) {
            return Observable.error(e);
          }
    
          return Observable.just(someType);
        }
      });
    }
    

    这个例子稍微复杂一些,将数据写进磁盘的同时如果抛出异常并捕获,则立即调用onError,基本的思路是相同的,那就是:在订阅发生之前,不希望执行任何代码。

    其实,有很多方式可以解决上面的问题,虽然使用defer()操作符只是其中之一,但是,使用起来真的很方便。

    相关文章

      网友评论

      • 一个不熬夜的孩子:defer 和 create有什么区别吗。按照文章说的就是defer比较方便,但是create不会每次都创建一个新的Observable。所以defer能做的,create也是可以做的吧。
        artshell:文中已经提到了, create 多了每次去主动调用onComplete()方法,另外RxJava 2.x中create()方法比defer()更强大,推荐使用RxJava 2.x
      • 666swb:有什么在android中实际的例子吗,使用defer()
      • sugaryaruan:酷,对于defer,看完的总结是:defer使得执行observable里的方法编程subscribe订阅之后
      • 接地气的二呆:这篇文章解决了我的疑惑 :sunglasses:
      • ebb7f2a77196:我试了一下,三种方式都能输出”Some Value“,不知道和环境有没有关系。
      • 天之大任:这两天一直在看你的文章,好多常用的rxjava都讲到了,厉害
      • 3f6796a0f464:请问有没有介绍BehaviorSubject的文章呢。
      • 6db2cd5e23ce:又学到新的姿势~ Thanks

      本文标题:【译】使用RxJava实现延迟订阅

      本文链接:https://www.haomeiwen.com/subject/dizwqttx.html