Почему только последний подписчик получает событие onNext в RxJava на Android с использованием Kotlin и RxMVP

Учитывая следующий сценарий:

View с CheckBox и несколькими EditText
View предлагает доступ к Observables с помощью RxBinding Джейка Уортона вот так

fun observeUsername(): InitialValueObservable<CharSequence> = RxTextView.textChanges(et_username)

для EditTexts (их три, для имени пользователя, пароля и электронной почты) и

fun observeSignUpCheckBox(): InitialValueObservable<Boolean> = RxCompoundButton.checkedChanges(cb_sign_up)

для CheckBox

Presenter имеет один метод на EditText, который так же прост, как

fun observeUsernameText(): Disposable {
    return view.observeUsernameText()
            .skipInitialValue()
            .map { username -> StringUtils.isValidUsername(username.toString()) }
            .subscribe({ view.setValidUsername(it) })
}

и один метод для CheckBox:

fun observeSignUpCheckBox(): Disposable {
    return view.observeSignUpCheckBox()
            .subscribe({ checked ->
                Timber.d("### view trigger")
            })
}

все эти методы вызываются в onCreate из Presenter и все работает как положено.

Теперь проблема:
я добавил новую функцию в Presenter, которая проверяет ввод пользователя:

fun observeInputFields(): Disposable {
    val email = view.observeEmailText().map { email -> StringUtils.isValidEmail(email.toString()) }
    val password = view.observePasswordText().map { password -> StringUtils.isValidPassword(password.toString()) }
    val signUp = view.observeSignUpCheckBox()
    val username = view.observeUsernameText().map { username -> StringUtils.isValidUsername(username.toString()) }
    return Observable.combineLatest(email, password, signUp, username,
            Function4<Boolean, Boolean, Boolean, Boolean, Boolean> { validEmail, validPassword, signUpChecked, validUsername ->
                validEmail && validPassword && (!signUpChecked || signUpChecked && validUsername)
            })
            .subscribe({ validForm ->
                Timber.d("### form trigger")
                view.enableContinue(validForm)
            })
}

Всякий раз, когда я изменяю содержимое EditText, оба Subscriptions (тот, что для EditText, и объединенный, добавленный в observeInputFields) получают событие, как и ожидалось.
Но если я нажимаю на CheckBox, только последний Subscription получает событие событие в зависимости от порядка функций в onCreate.

fun onCreate() {
    // here the logs only show '### form trigger'
    disposables.add(observeSignUpCheckBox())
    disposables.add(observeInputFields())
    // ... omitted for clarity
}

or

fun onCreate() {
    // here the logs only show '### view trigger'
    disposables.add(observeInputFields())
    disposables.add(observeSignUpCheckBox())
    // ... omitted for clarity
}

Я не могу понять, почему эта странная вещь происходит только для CheckBox, но не для EditText. Это сильно сбивает с толку...

Любая помощь очень ценится, так как я в настоящее время застрял на этом :(

Спасибо!


person grAPPfruit    schedule 02.10.2017    source источник


Ответы (1)


Если вы проверите исходный код библиотеки здесь и здесь вы увидите, что то, что вы описываете, является ожидаемым поведением. Для aCompoundButton вы можете иметь только один Observable за раз, но для aTextView несколько.

Если вы хотите пойти еще дальше, вы можете увидеть, что здесь, чтобы создать Observable, вам нужно использовать setOnCheckedChangeListener на CompoundButton, который каждый раз заменяет возможный существующий прослушиватель.

С другой стороны, для TextView вы можете добавить несколько TextWatchers и удалить каждый по отдельности onDispose.

person masp    schedule 03.10.2017
comment
Знаешь, теперь, когда ты объяснил это, это имеет смысл. Я читал документацию, где говорится, что Только один наблюдаемый объект может использоваться для представления за раз. но, поскольку я новичок в RX, я не осознавал, что использую несколько наблюдаемых. Я думал, подписавшись на один, я всегда получаю ссылку на один и тот же?! Теперь, есть ли элегантный способ обойти этот факт! - person grAPPfruit; 03.10.2017
comment
Вам нужно будет разделить выбросы Observable, например, с помощью ConnectableObservable. Проверьте это - person masp; 03.10.2017
comment
Спасибо @masp! Что я сделал сейчас: 1. Я изменил метод View на funObservSignUpCheckBox(): ConnectableObservable‹Boolean› = RxCompoundButton.checkedChanges(cb_sign_up).publish(), 2. Presenter получил новый член: val signUpConnectable: ConnectableObservable‹Boolean› с помощью lazy { view.observeSignUpCheckBox() и 3. в onCreate() в Presenter я добавил signUpConnectable.connect() в качестве последней строки. Кажется, это работает. Любые рекомендации здесь или это нормально? - person grAPPfruit; 03.10.2017
comment
Не забывайте, что signUpConnectable.connect возвращает disposable. Об этом нужно позаботиться. - person masp; 03.10.2017
comment
Ты понял! Метод onCreate() изменен на disposables.add(signUpConnectable.connect()) Спасибо! - person grAPPfruit; 03.10.2017