I've been burned by the share() operator.

I was implementing a login form validation using RxJava and I had to share an Observable to multiple observers. Here is what I had at the beginning:

// 1. Observable that emits true if the email address is valid. Observable emailValid = RxTextView.textChanges(emailTextView) .map(VALIDATE_EMAIL) .share(); // 2. Show an error on the email field if the email is invalid and it's not focused Observable.combineLatest(emailValid, RxView.focusChanges(emailTextView), (emailIsValid, hasFocus) -> emailIsValid || hasFocus) .subscribe(emailErrorAction); // 3. Enable the submit button if both the email and password are valid. Observable.combineLatest(emailValid, passwordValid, (emailValid, passwordValid) -> emailValid && passwordValid) .subscribe(enableSubmitButton));

Except I had an issue, the submit button would start off enabled (even though both the email field and the password fields were empty). In order to disable the submit button I had to enter text into both the email field and the password field.

Weird.

It turns out there is a small race condition caused by the use of share() . Since share() begins emitting items as soon as the first Observer subscribes Observable #3 doesn't receive the initial value of the emailTextView , but Observable #2 does.

To solve this we need to wait until all observers have subscribed before connecting to Observable #1.

// 1. Observable that emits true if the email address is valid. ConnectableObservable emailValid = RxTextView.textChanges(emailTextView) .map(VALIDATE_EMAIL) .publish(); // 2. Show an error on the email field if the email is invalid and it's not focused Observable.combineLatest(emailValid, RxView.focusChanges(emailTextView), (emailIsValid, hasFocus) -> emailIsValid || hasFocus) .subscribe(emailErrorAction); // 3. Enable the submit button if both the email and password are valid. Observable.combineLatest(emailValid, passwordValid, (emailValid, passwordValid) -> emailValid && passwordValid) .subscribe(enableSubmitButton)); emailValid.connect()

Now Observable #3 gets the initial value of the emailValid Observable and the button is enabled/disabled properly.

TL;DR

Only use share() when you don't know how many subscribers you might have and you don't care if some subscribers miss values.

See