RxJava, one observable multiple subscribers: publish().autoConnect()

前端 未结 2 1317
独厮守ぢ
独厮守ぢ 2021-02-07 05:36

I\'m playing around with rxJava/rxAndroid and there\'s something very basic that doesn\'t behave as I\'d expect. I have this one observable and two subscribers:

         


        
2条回答
  •  天涯浪人
    2021-02-07 06:12

    Regular (cold) observable

    At the heart of Observable is subscribe function. Every time new observer subscribes it is passed to this function as a parameter. What this function does, it feeds data into that single observer. It does this by calling observer.onNext method. It might do this immediately (like just does), or via some scheduler (eg. interval), or from background thread or callback (eg. by launching some async task).

    I highlighted word single above because that is the only observer this function knows about when it is invoked. In case you subscribe to such an observable multiple times, its subscribe function is called for every subscriber.

    Data source like this is called cold observable.

    Schedulers

    Applying subscribeOn operator adds intermediate step between your subscribe call and original observable's subscribe function. You are no longer calling it directly, but schedule your call via specified scheduler.

    observeOn adds similar intermediate step to all onNext invocations of your observer.

    In your example subscribe function is called twice, i.e. data series get generated twice. The calls are scheduled via multi-threaded io scheduler, so these invocations happen not on main thread but on two other threads, almost simultaneously. Both threads start invoking onNext methods of two subscribers. Remember that each thread knows about its own subscriber only. onNext calls get scheduled by mainThread scheduler, which is single threaded, i.e. they cannot happen simultaneously but need to be queued somehow. Strictly speaking there can be no guarantee about the ordering of these calls. It depends on various factors and is implementation specific. Try replacing just with interval (this will introduce delay between messages) and you'll see that messages will arrive in different order.

    Hot observable

    publish operator makes your observable hot, aka connectable. It adds intermediate steps to both subscribe function - this is called only once, and to onNext methods - these get propagated to all subscribed observables. In other words, it allows multiple subscribers to share single subscription.

    To be precise, subscribe function is called when you invoke connect method. There are two operators that invoke connect automatically for you:

    • autoConnect invokes connect method when first subscriber comes in. It never disconnects though.
    • refCount invokes connect when first subscriber comes in, and automatically disconnects when last subscriber unsubscribes. It will re-connect (call subscribe function again) when new subscribers come in.

    publish().refCount() is popular combination, so it has got shortcut: share().

    For your education try the following code with and without share:

    Observable dataStream = Observable.interval(100, TimeUnit.MILLISECONDS)
            .take(3)
            .share();
    System.out.println("subscribing A");
    dataStream.subscribe(v -> System.out.println("A got " + v));
    TimeUnit.MILLISECONDS.sleep(150);
    System.out.println("subscribing B");
    dataStream.subscribe(v -> System.out.println("B got " + v));
    TimeUnit.SECONDS.sleep(1);
    

    Answers to original questions

    1) Cold observable always deals with single subscriber. So your time diagrams shall look like this:

    subscribed first subscriber
    [SUBSCRIBER: 1][ITEMS TO EMIT: 1,2,3]
    subscribed second subscriber
    [SUBSCRIBER: 1][ITEMS TO EMIT: 1,2,3]
    [SUBSCRIBER: 2][ITEMS TO EMIT: 1,2,3]
    emit "1" to subscriber 1
    [SUBSCRIBER: 1][ITEMS TO EMIT: 2,3]
    [SUBSCRIBER: 2][ITEMS TO EMIT: 1,2,3]
    emit "1" to subscriber 2
    [SUBSCRIBER: 1][ITEMS TO EMIT: 2,3]
    [SUBSCRIBER: 2][ITEMS TO EMIT: 2,3]
    ...
    

    Although the order is not guaranteed due to multi-threading races.

    2) publish and autoConnect do not cancel each other. They only add.

    dataSource = ...;
    dataSourceShared = dataSource.publish().autoConnect();
    

    Now when you subscribe multiple subscribers to dataSourceShared this results in only one subscription to original dataSource. I.e. you do not have to emit new series of messages for every new subscriber.

提交回复
热议问题