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. ObservableemailValid = 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. ConnectableObservableemailValid = 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.