I've seen patters like this:
Observable<String> nameChanges = nameDataSource.changes().share();
// One subscriber
autoUnsubscribe(nameChanges.subscribe(() -> { ... }));
// Another subscriber
autoUnsubscribe(nameChanges.map(...).filter(...).subscribe(...));
// autoUnsubscribe is called when the UI is torn down
My question is:
Why is it necessary to call share() whenever I want to listen to the Observable in multiple places?
Why is not share()
the default behavior for all observables?
It would be nice if the code above worked the same even without .share()
. I would not have to think about when I need to share an Observable, and when I don't.
Is it for performance reasons because having a single subscriber is a special case that can be handled more efficiently?
From the docs this is not clear to me:
share() returns a new ObservableSource that multicasts (shares) the original ObservableSource. As long there is at least one Observer this ObservableSource will be subscribed and emitting data.
There are two kinds of
Observable
s: cold and hot. ColdObservable
s start producing items when there is anObserver
to them and they do it on an individual basis. Subscribing with multipleObserver
s will result in multiple runs of the same coldObservable
. An example of this is anObservable
that issues a network request.In contrast, a hot
Observable
can produce items with or without the presence ofObserver
s. TheseObservable
s usually multicast the same items to all of their currentObserver
s. An example of this is anObservable
of button clicks.To turn a cold
Observable
into a hot one, you can usepublish()
which will make sure only a single subscription is established to the sourceObservable
no matter how manyObserver
s there are. Thus, the chain will now act as a hot multicastingObservable
from theObserver
s' perspective.However, often it is not economic to keep an originally cold
Observable
running after allObserver
s ofpublish()
have gone away, therefore therefCount()
operator can be used to keep track of them and stop the source when there are no interesting parties left.If your source is already hot, you don't need
share()
.Actually, this property is one of the important contributions of the modern reactive programming paradigm: the distinction of the hot and cold
Observable
s.However, multicasting is expensive on itself because you have to keep track of the set of current
Observer
s in order to notify them with new events, and this can lead to all sorts of logical race conditions to be defended against. A coldObservable
is known to talk only to a singleObserver
so there is no need for the extra tracking overhead.I would not have to think about when I need to share an Observable, and when I don't.
I'm afraid that you will have to think when programming. ;)In the above code, yes, sharing makes sense. But what if you we're updating something on the backend, using the same network and method call from two different points, updating with different data? You would have the same call with the same Observable, but you certainly wouldn't want to share the Observable instance (and the network call) because that means that all successive calls with data would be discarded in the favour of the first one. Or if you wanted to start a timer whenever you're subscribed, you again wouldn't want to share this with other Observables.
Also, it is much easier to add certain behaviour than to remove it. For example, if sharing was the default, and the second subscriber wouldn't want to share and removes the sharing - what about the third one? Will it share it with the first one or not? At that point the sharing property becomes the property of the
Subscriber
and not theObservable
and you would have no clear way of knowing which one is shared and which one isn't.