RxJava Observable.doOnUnsubscribe()vs Subscriber.add()

时间:2016-11-02 16:21:07

标签: rx-java

我有时必须在我的Observables中做一些清理任务(例如关闭打开的文件),我想知道什么是最好的方法。 到目前为止,我已经看过两个,但我很难理解他们的不同之处:你能解释一下这些差异吗?有没有更好的方法来实现这个目标?

1)

    // MyObject will take care of calling onNext(), onError() and onCompleted()
    // on the subscriber.
    final MyObject o = new MyObject();

    Observable obs = Observable.create(new Observable.OnSubscribe<Object>() {
        @Override
        public void call(Subscriber<? super Object> subscriber) {
            try {
                if (!subscriber.isUnsubscribed()) {

                    o.setSubscriber(subscriber);

                    // This will tell MyObject to start allocating resources and do its job.
                    o.start();

                }
            } catch (Exception e) {
                subscriber.onError(e);
            }
        }
    }).doOnUnsubscribe(new Action0() {
        @Override
        public void call() {
            // This will tell MyObject to finish its job and deallocate any resources.
            o.stop();
        }
    });

2)

    Observable obs = Observable.create(new Observable.OnSubscribe<Object>() {
        @Override
        public void call(Subscriber<? super Object> subscriber) {
            try {
                if (!subscriber.isUnsubscribed()) {

                    // MyObject will take care of calling onNext(), onError() and onCompleted()
                    // on the subscriber.
                    final MyObject o = new MyObject(subscriber);

                    subscriber.add(Subscriptions.create(new Action0() {
                        @Override
                        public void call() {
                            // This will tell MyObject to finish its job and deallocate any resources.
                            o.stop();
                        }
                    }));

                    // This will tell MyObject to start allocating resources and do its job.
                    o.start();

                }
            } catch (Exception e) {
                subscriber.onError(e);
            }
        }
    });

4 个答案:

答案 0 :(得分:2)

要回答原始问题,doOnUnSubscribe和向Subscription添加Subscriber都是一样的。事实上,当您致电doOnUnSubscribe时,只需将Action作为Subscription添加到您的Subscriber。因此,doOnUnSubscribe在后​​台使用了您的第二个示例。

doOnUnSubscribe代码:

public class OperatorDoOnUnsubscribe<T> implements Operator<T, T> {
  private final Action0 unsubscribe;

/**
 * Constructs an instance of the operator with the callback that gets invoked when the modified Observable is unsubscribed
 * @param unsubscribe The action that gets invoked when the modified {@link rx.Observable} is unsubscribed
 */
public OperatorDoOnUnsubscribe(Action0 unsubscribe) {
    this.unsubscribe = unsubscribe;
}

@Override
public Subscriber<? super T> call(final Subscriber<? super T> child) {
    child.add(Subscriptions.create(unsubscribe));

    // Pass through since this operator is for notification only, there is
    // no change to the stream whatsoever.
    return Subscribers.wrap(child);
  }
}

答案 1 :(得分:1)

您提到的两个解决方案中使用 的决定取决于您尝试使用/关闭/部署的资源是否要在多个订阅之间共享 即可。

  1. 使用资源生成事件时,使用 subscriber.add(...)。在这种情况下,您不希望共享资源。

    • MyObject的示例就是这种情况。这样做的好处是资源不会在Observable.create()方法之外暴露,从而使资源免于意外的副作用。
  2. 当您必须在多个订阅中共享内容时,请使用 doOnUnsubscribe

    • 例如,如果您想要使用Observable使用次数的计数器,您可以拥有一个共享计数器并继续在doOnUnsubscribedoOnSubscribe中递增。
    • 另一个例子可能是,如果您想要计算当前对资源开放的连接数的计数器,您可以在doOnSubscribedoOnUnsubscribe中相应地使用递增和递减组合来实现该目的。
  3. 同样在您的示例中,您可以使用MyObject方法替换它来管理资源的打开和关闭以及生成事件,而不是创建Observable.using()抽象,以实现相同的目标。它有三个参数:

    • resourceFactory,将打开资源
    • observableFactory,将生成事件和
    • disposeAction,将关闭资源

答案 2 :(得分:0)

如果你没有在subscriber.onNext下面放置任何值,那么使用Observable.create有什么意义呢?

第一个是一个巨大的禁忌,因为你正在为一个被关闭的物体做副作用。如果您同时从两个不同的线程订阅创建的observable会发生什么?

第二个看起来更好,因为你添加了subscriber.add,如果已经处理了订阅,它将调用o.stop()。唯一缺少的是onNext的调用,该值遍历下游。

有一个运算符用于从资源创建Observables,称为“using”。请查看http://reactivex.io/RxJava/javadoc/rx/Observable.html#using(rx.functions.Func0,%20rx.functions.Func1,%20rx.functions.Action1)

答案 3 :(得分:0)

如果你可以提供帮助,首先不要使用Observable.create(OnSubscribe),因为你可以很容易地破解东西(比如背压或可观察合同相关的东西)。您应该使用许多静态工厂方法之一。

除了直接解决您的问题外,我建议Observable.using明确设计为在终止或取消订阅时释放资源。

例如:

Observable<byte[]> bytes = 
  Observable.using(
    () -> new FileInputStream(file), //resource factory
    is -> Bytes.from(is), // observable factory
    is -> is.close() // close action
  );    

如果您使用RxJava 2,上面的示例会错过一些不会出现的尝试捕获(例如is.close()左右)。

试图充实你的案子:

Observable.using(
  () -> new MyObject(), //resource factory
  myObject -> makeObservable(myObject), // observable factory
  myObject -> myObject.stop() // close action
);