暴露的最佳做法"昂贵的" RxJava中的可观察量

时间:2015-05-22 12:41:11

标签: rx-java

我是RxJava的新手,并试图确定常见的习语和最佳做法。

说我已经有Foo课程发出Bar s(目前不完整且过于简化):

class Foo {
    public Subscriber barSubscriber;
    public Observable<Bar> getBarObservable = (...details omitted...)

    private void someMethod() {
        // emit a Bar
        barSubscriber.onNext(bar); 
    }
}

想要订阅Bars的其他对象通过调用

来实现
foo.getBarObservable().subscribe(...);

让我们说生产和发出Bar是昂贵的&#34;。为了避免在没有订阅者的情况下执行此操作,Foo的getBarObservable可以公开一个可连接的,重新计算的Observable,如此(使用share()):

class Foo {
    private Subscriber barSubscriber;
    private Observable<Bar> barObservable =  Observable.create(
            new Observable.OnSubscribe<Bar>() {
                @Override
                public void call(Subscriber<? super Bar> subscriber) {
                    Foo.this.subscriber = subscriber;
                    new Thread(new Runnable() {
                        @Override
                        public void run() {
                            runUntilUnsubscribed();
                        }
                    }).start();

                }
            }
    ).share();

    public Observable<Bar> getBarObservable() {
        return barObservable;
    }

    public void runUntilUnsubscribed(} {
        while(!subscriber.isUnsubscribed()) {

            /* do some heavy stuff that produces a Bar.  If, when a 
               Bar is ready, we still have subscribers, emit the Bar */

            if (!subscriber.isUnsubscribed()) 
                subscriber.onNext(bar);
        }
    }
}

我见过的大多数示例和教程都会在订阅它们的相同代码块中即时创建Observable内联,所以我不清楚它是什么标准做法是在更真实的场景中,Observable的创建及其订阅位于两个不同的地方。

  1. 对于像Foo这样一个不想知道订阅者是谁或者会有多少订阅者的类,这是正确的方法吗?
  2. 在我看来,这将是一个非常典型的场景 - 是吗?或者,从较高的层面来看,这不是考虑揭露Observable的正确方法吗?常规使用这种方法有缺点吗?
  3. 在我看来,每次想要发出if (subscriber == null && !subscriber.isUnsubscribed()) subscriber.onNext(bar);时,我都需要那个小Bar模式。这也是一个常见的习语,还是有更好的方法?没关系,我不需要空检查......不知道我在想什么。

2 个答案:

答案 0 :(得分:3)

  1. 是的,这大致是正确的做法。如果bar中的Foo需要与所有订阅者共享,请使用.publish().refCount()(或share(),如您所说)。如果没有,那么使用一个常见的Observable,默认情况下是“冷”。

  2. 公开Observable是一种常见的情况。在一个良好的反应式架构中,大多数类只有Observables的getter,因为setter本身不具有反应性。给定一个与setter一起使用的程序或类,通常可以将它转换为Observables和getter而不影响功能。由于一些控制反转,可观测量和吸气剂是一种理想的方法。使用setter,如果FooBaz中设置了值,则只要您想了解Foo,就需要查看课程Baz。但是对于Observables和getters,Baz来自FooBaz定义了它的工作原理,而Foo可以忽略Baz

  3. 我从未需要使用if模式。我也很少需要Observable.create()。有许多Observable创建助手(fromintervalrangejust,仅举几例)和Observable转换(例如全能{{1} })允许你在表达新的Observables方面取得很大进展。此外,主题允许您在旅途中手动创建Observable。

答案 1 :(得分:3)

您的示例类无法正常工作:如果订阅者为setBar,则null可以抛出NPE,runUntilUnsubscribed引用缺少的bar字段/值,并且是繁忙的循环将反复发出相同的值。

你说创建一个Bar是昂贵的,但它的创建似乎超出了Foo类,我想你想要将这样的价值发送给当前订阅的订阅者。这就是PublishSubject的用途:

class Foo {
    final PublishSubject<Bar> subject = PublishSubject.create();
    public void setBar(Bar bar) {
        subject.onNext(bar);
    }
    public Observable<Bar> getBarObservable() {
        return subject; // .asObservable() if you want to hide the subject
    }
}

如果没有任何订阅者,那么条形图集就会掉线并收集垃圾。如果您想保留最后一个值,请使用BehaviorSubject代替PublishSubject

否则,如果您需要在订阅者到达时触发创建昂贵的Bar值,您可以使用share()的一些跳转启动序列:

Observable.just(1)
.subscribeOn(Schedulers.computation())
.map(v -> createBar())
.share();

share()的使用实际上取决于每个Bar值的预期生命周期。

例如,如果您希望存储条形码直到订阅者到达,然后执行繁重的计算并分发结果,您可以使用以下构造:

class Foo {
    final BehaviorSubject<Bar> subject = BehaviorSubject.create();
    final Observable<Bar> output = subject
        .observeOn(Schedulers.computation())
        .doOnNext(bar -> expensiveInplaceComputation(bar))
        .take(1)
        .share();

    public void setBar(Bar bar) {
        subject.onNext(bar);
    }
    public Observable<Bar> getBarObservable() {
        return output;
    }
}

有关可运行的示例,请参阅this gist