I assume you have a hot observable with multiple subscriptions, but only want one subscription to receive events. Once the current subscription unsubscribes, the next one should start receiving.
What we can do is give every subscription and unique number, and keep a list of all subscriptions. Only the first subscription in the list receives events, the rest of them filter
s the events away.
public class SingleSubscribe {
List<Integer> current = Collections.synchronizedList(new ArrayList<>());
int max = 0;
Object gate = new Object();
private SingleSubscribe() {
}
public static <T> Transformer<T, T> singleSubscribe() {
return new SingleSubscribe().obs();
}
private <T> Transformer<T, T> obs() {
return (source) -> Observable.create((Subscriber<? super T> ob) -> {
Integer me;
synchronized (gate) {
me = max++;
}
current.add(me);
source.doOnUnsubscribe(() -> current.remove(me)).filter(__ -> {
return current.get(0) == me;
}).subscribe(ob);
});
}
Example usage:
public static void main(String[] args) throws InterruptedException, IOException {
ConnectableObservable<Long> connectable = Observable.interval(500, TimeUnit.MILLISECONDS)
.publish();
Observable<Long> hot = connectable.compose(SingleSubscribe.<Long> singleSubscribe());
Subscription sub = connectable.connect();
Func1<Integer, rx.Observer<Long>> obs = (Integer i) -> Observers.create(el -> System.out.println(i + "next: " + el),
er -> {
System.out.println(i + "error: " + er);
er.printStackTrace();
} , () -> System.out.println(i + "completed"));
System.out.println("1");
Subscription s = hot.subscribe(obs.call(1));
System.out.println("2");
hot.take(4).subscribe(obs.call(2));
Thread.sleep(1500);
s.unsubscribe();
System.out.println("3");
Thread.sleep(500);
hot.take(2).subscribe(obs.call(3));
System.out.println("4");
System.in.read();
sub.unsubscribe();
}
}
Output:
1
2
1next: 0
1next: 1
1next: 2
3
2next: 3
4
2next: 4
2next: 5
2next: 6
2completed
3next: 6
3next: 7
3completed
Notice there is a small flaw in the output: Because 2 unsubscribes right after it receives 6, but before 3 receives 6. After 2 unsubscribes, 3 is the next active observer and happily accept 6.
A solution would be to delay doOnUnsubscribe
, easiest way is to schedule the action on a new thread scheduler:
source.doOnUnsubscribe(() -> Schedulers.newThread().createWorker().schedule(()-> current.remove(me)))
However, this means there is now a small chance the next item emitted is ignored when 2 unsubscribe, and the next item arrives before 3 is activated. Use the variation that is most suitable for you.
Lastly, this solution absolutely assumes the source to be hot. I'm not sure what will happen when you apply this operator to a cold observable, but the results could be unexpected.