简体   繁体   中英

How do I control the subscription count in RxJava 2?

I have a long running task (say an Observable<Integer> ) that I want to trigger as few times in my application as possible. I have multiple "views" on the task that process the events that it sends in various ways. I only have one subscribe in my entire application.

How do I ensure that the long running task is only triggered once for each subscription, and is only triggered when required by a subscription?

To make things more concrete, here is a unit-test:

@Test
public void testSubscriptionCount() {

    final Counter counter = new Counter();

    // Some long running tasks that should be triggered once per subscribe
    final Observable<Integer> a = Observable.just(1, 2, 3, 4, 5)
        .doOnSubscribe(subscription -> {
            counter.increment();
        });

    // Some "view" on the long running task
    final Observable<Integer> b = a.filter(x -> x % 2 == 0);

    // Another "view" on the long running task
    final Observable<Integer> c = a.filter(x -> x % 2 == 1);

    // A view on the views
    final Observable<Integer> d = Observable.zip(b, c, (x, y) -> x + y);

    d.toList().blockingGet();

    assertEquals(1, counter.count); // Fails, counter.count == 2
}

I would like a to only be triggered when one of its views ( b , c or d ) is subscribed to, but also only once per subscription.

In the code above, the subscription happens twice (I presume that d triggers b and c , which both trigger a independently).


Adding .share() does not solve the problem (although I think it is along the right lines):

    // Some long running tasks that should be triggered once per subscribe
    final Observable<Integer> a = Observable.just(1, 2, 3, 4, 5)
        .doOnSubscribe(subscription -> counter.increment())
        .share();

java.lang.AssertionError:

Expected :1

Actual :2

If your goal is to prevent multiple executions when the observer is subscribed in parallel, .share() is what you are looking for:

Observable<Integer> shared = source.share();

// In thread 1:

shared.subscribe(...);

// In thread 2:

shared.subscribe(...);

So long as the source observable has not yet completed when second subscription happens, it will receive the same results as the first, and will not force another execution of the source observable.

The RxJava documentation has much more detailed explanation, but it's basically a wrapper that has some reference counting and only subscribes to the source observable when necessary to avoid concurrent executions.

Also keep in mind that timing will play an important part in which values are actually delivered. I don't believe .share() will do any specific buffering of elements, so if elements are delivered prior to the second subscription the second subscription will not get those elements. You'd have to use .buffer() or some other means of holding onto results for late subscribers.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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