简体   繁体   English

RxJava。 订阅开始时的初始onNext?

[英]RxJava. Initial onNext when subscription starts?

I'm trying to implement a class that emits changes using an Observable. 我正在尝试实现一个使用Observable发出更改的类。 When a subscription is done to this observable I want to send an starting/initialization event. 当订阅到此可观察对象时,我想发送一个启动/初始化事件。 Then I want to send the usual events. 然后,我想发送平常的事件。

For example. 例如。 Lets say I have two different subscribers A and B. A and B starts subscribing at different times. 可以说我有两个不同的订户A和B。A和B在不同的时间开始订阅。 If MyClass.getChanges() emits event no. 如果MyClass.getChanges()发出事件号。 1,2,3,4 and 5. 1,2,3,4和5

If A starts it subscription between event 1,2 then it should receive the following events: InitialEvent, 2, 3, 4, 5. 如果A在事件1,2之间开始订阅,则它应接收以下事件:InitialEvent,2、3、4、5。

If B starts it subscription between event 4 and 5, then B should receive the following events: InitialEvent, 5. 如果B在事件4和5之间开始订阅,那么B应该收到以下事件:InitialEvent,5。

How to do this using RxJava? 如何使用RxJava做到这一点?

Thanks! 谢谢!

Edit 1 编辑1

I think I need to explain that the "InitialEvent" is different each time it's emitted. 我想我需要解释一下,每次发出“ InitialEvent”都是不同的。 It's calculated by MyClass each time a new subscriber starts to subscribe from getChanged(). 每次新订阅者开始从getChanged()进行订阅时,它都是由MyClass计算的。

My scenario is that MyClass contains a list. 我的情况是MyClass包含一个列表。 The "initialEvent" contains the list at the moment when the subscription is done. 订阅完成后,“ initialEvent”将包含该列表。 Then each change to this list is emitted from getChanges(). 然后,从getChanges()发出对此列表的每次更改。

What you're looking for is PublishSubject . 您正在寻找的是PublishSubject Subjects are hot Observables , in that they do not wait for Observers to subscribe to them before beginning to emit their items. 主题是热门的Observables ,因为它们在开始发出项目之前不等待Observers进行subscribe Here's a bit of info on Subjects . 以下是有关Subject的一些信息。

Here's a quick demo of your use-case 这是您的用例的快速演示

    PublishSubject<String> subject = PublishSubject.create();
    Observable<String> InitEvent = Observable.just("init");
    Observable<String> A = subject.asObservable();
    Observable<String> B = subject.asObservable();

    subject.onNext("1");

    A.startWith(InitEvent)
            .subscribe(s -> System.out.println("A: " + s));

    subject.onNext("2");
    subject.onNext("3");
    subject.onNext("4");

    B.startWith(InitEvent)
            .subscribe(s -> System.out.println("B: " + s));

    subject.onNext("5");

Possibly not really elegant way how about just using a flag? 可能不是很优雅的方式,仅使用标志呢? It looks like you just want to replace the first emitted event. 看来您只想替换第一个发出的事件。

eg for one subscription the following logic: 例如,对于一个订阅,以下逻辑:

boolean firstTimeA = true;

myCustomObservable.subscribe(s -> {
   System.out.println(firstTimeA ? "initEvent" : s.toString());
   if(firstTimeA) firstTimeA = false;
});

And since you want to have a second subscription just create a firstTimeB and update it your B subscription. 并且由于您想要第二个订阅,只需创建一个firstTimeB并将其更新为您的B订阅即可。

If I understand what you are asking something like this should work for you 如果我了解您的要求,这样的事情应该对您有用

int last = 0;
Observable obs;
List<Integer> list = new ArrayList<>();

public SimpleListObservable() {
    obs = Observable.create(new Observable.OnSubscribe<Integer>() {
        @Override
        public void call(Subscriber<? super Integer> subscriber) {
            while(last < 30) {
                last++;
                list.add(last);
                subscriber.onNext(last);
            }
            subscriber.onCompleted();
        }
    });
}

public Observable<Integer> process() {
    return Observable.from(list).concatWith(obs);
}

As the source observable collects values they are added to the List (you can transform the items as you see fit, filter them out, etc) and then when ObserverB subscribes it will get a replay of the items already collected in the List before continuing with the source observable output. 当源Observable收集值时,它们将被添加到List (您可以按自己认为合适的方式变换项目,将其过滤掉等),然后当ObserverB订阅时,它将重放List已收集的项目,然后继续操作。源可观察的输出。

This simple test should demonstrate the outcome 这个简单的测试应该证明结果

public void testSequenceNext() {
    final SimpleListObservable obs = new SimpleListObservable();

    final Observer<Integer> ob2 = Mockito.mock(Observer.class);

    obs.process().subscribe(new Observer<Integer>() {
        @Override
        public void onCompleted() {
            ob1Complete = true;
        }

        @Override
        public void onError(Throwable e) {
            e.printStackTrace();
        }

        @Override
        public void onNext(Integer integer) {
            System.out.println("ob1: " + integer);
            if (integer == 20) {
                obs.process().subscribe(ob2);
            }
        }
    });

    ArgumentCaptor<Integer> captor = ArgumentCaptor.forClass(Integer.class);

    Mockito.verify(ob2, Mockito.times(30)).onNext(captor.capture());

    for (Integer value : captor.getAllValues()) {
        System.out.println(value);
    }

}

What do you think of this, I've made part of my API of course as I'm on a phone : 您对此有何看法,我在打电话时当然已经成为API的一部分:

public class StreamOfSomething {
    new StreamOfSomething() {
        // source of events like
        events = Observable.range(0, 1_000_000_000)
                           .doOnNext(set::add) // some operation there
                           .map(Event::change)
                           .publish()
                           .refCount();
    }

    public Observable<Event> observeChanges() {
        return events.startWith(
                 Observable.just(Event.snapshot(set))); // start stream with generated event
    }
}

And the client can do something like : 客户可以执行以下操作:

Observable.timer(2, 4, TimeUnit.SECONDS)
          .limit(2)
          .flatMap(t -> theSourceToWatch.observeChanges().limit(10))
          .subscribe(System.out::println);

Note however if you are in a multithreaded environment you may have to synchronize when you are subscribing to block any modification, otherwise the list may change before it get's emitted. 但是请注意,如果您在多线程环境中,则在订阅阻止任何修改时可能必须同步,否则列表可能会在发出之前更改。 Or rework this class completely around observables, I don't know yet how to achieve this though. 还是完全围绕可观察变量重做该类,尽管我还不知道该如何实现。

Sorry to post this 2 years later, but I had the same need and found this question unanswered. 很抱歉在两年后发布,但是我有同样的需求,发现这个问题没有答案。

What I did is the following: 我所做的是以下几点:

public Observable<Event> observe() {
    return Observable.defer(() -> 
        subject.startWith(createInitialEvent())
    );
}

The idea is the following: 这个想法如下:

  • defer() executes the passed-in lambda expression when an observer subscribes to the Observable returned by the method observe(). 当观察者订阅由方法observe()返回的Observable时,defer()执行传入的lambda表达式。 So basically, it executes subject.startWith(...), which returns an Observable that is the actual source of event for the subscriber. 因此,基本上,它执行subject.startWith(...),它返回一个Observable,它是订户的实际事件源。
  • subject.startWith(...) emits an initial event (specified by startWith(...)) followed by those emitted by the subject. subject.startWith(...)发出一个初始事件(由startWith(...)指定),然后发出由主题发出的事件。

So, if I come back to the original post: if an observer starts it subscription between event 1,2 then it should receive the following events: InitialEvent, 2, 3, 4, 5. 因此,如果我回到原始帖子:如果观察者在事件1,2之间启动订阅,那么它应该收到以下事件:InitialEvent,2、3、4、5。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM