[英]Using RxJava for email login validation, an observable is emitting twice
我正在制作一個簡單的登錄表單(電子郵件和密碼)來嘗試加強我的反應式編程技能組合。 我在使電子郵件字段驗證按照我想要的方式工作時遇到了一些麻煩。
這是我的代碼:
final Observable<CharSequence> email = RxTextView.textChanges(emailView);
Observable<Boolean> emailIsValid = email.map(new Func1<CharSequence, Boolean>() {
@Override
public Boolean call(CharSequence charSequence) {
Log.d("asdf", "emailIsValid call: " + charSequence);
return Pattern.matches(Patterns.EMAIL_ADDRESS.pattern(), charSequence);
}
});
RxView.focusChanges(emailView)
.withLatestFrom(emailIsValid, new Func2<Boolean, Boolean, Boolean>() {
@Override
public Boolean call(Boolean hasFocus, Boolean emailIsValid) {
return (!hasFocus && !emailIsValid);
}
})
.subscribe(new Action1<Boolean>() {
@Override
public void call(Boolean showError) {
if (showError) {
emailInputLayout.setError("Enter a valid email");
} else {
emailInputLayout.setError(null);
}
}
});
Observable<CharSequence> password = RxTextView.textChanges(passwordView);
Observable.combineLatest(emailIsValid, password,
new Func2<Boolean, CharSequence, Boolean>() {
@Override
public Boolean call(Boolean emailIsValid, CharSequence password) {
Log.d("asdf", "valid: " + emailIsValid + ", password: " + password);
return (emailIsValid && password.length() > 0);
}
})
.subscribe(RxView.enabled(loginButton));
這是日志:
emailIsValid call: emailIsValid call: valid: false, password: // I type 'j' emailIsValid call: j emailIsValid call: j valid: false, password: // I type 'a' emailIsValid call: ja emailIsValid call: ja valid: false, password:
正如您所看到的,每次鍵入一個字符時都會調用emailIsValid
兩次,這意味着它正在進行兩次正則表達式匹配,這有點浪費。
我查看了如何使emailIsValid
每次更改只調用一次,無論它有多少訂閱者,我找到了share()
方法。 這是當我將.share()
添加到emailIsValid
的聲明結尾時會發生什么:
emailIsValid call: // I type 'j' emailIsValid call: j valid: false, password: // I type 'a' emailIsValid call: ja valid: false, password:
這解決了問題,但它導致了另一個問題: emailIsValid
沒有初始發出到emailIsValid
的combineLatest
函數,因此Login按鈕啟動時應該被禁用(灰顯)。
解決這個問題的最簡潔方法是什么? 我想我希望它表現得像一個BehaviorSubject
,但我不確定這是否是最好的方法。
我想這里發生的事情如下:
第一個subscribe
- 在RxView.focusChange()...
結尾處的訂閱 - 導致訂閱到emailIsValid
(因此也email
到email
)。
然后, email
將立即將TextView
的當前內容作為其第一項發出,然后通過emailIsValid
進行share
並share
給第一個Subscriber
(即withLatestFrom
運算符)。
一段時間后, combineLatest
導致另一個訂閱到emailIsValid
。 由於emailIsValid
是share
d,因此此訂閱不會“通過”到email
,因此每個項目仍將只發出一次。
現在的問題是, share
行為類似於PublishSubject
:它只是向所有訂閱者發出任何未來事件,但它不會重放任何過去的事件。
總之,這意味着:當第二個Subscriber
( combineLatest
)到達時,初始值已經過去 - 它是在第一個訂閱之后立即發出的。 並且只有在更改 TextView
的內容時才會到達下一個值。
解決方案:在emailIsValid
的末尾嘗試replay(1).refCount()
而不是share()
- 這應該確保每個新的訂閱者也接收上一個先前的評估結果以及所有未來的評估結果。
我希望能解決問題並且我的解釋是有道理的。
您可以使用publish()
和connect()
。
val email = RxTextView.textChanges(emailEditText)
val emailIsValid = email.map { charSequence ->
Log.d("asdf", "emailIsValid call: " + charSequence)
Pattern.matches(Patterns.EMAIL_ADDRESS.pattern(), charSequence)
}.publish()
RxView.focusChanges(emailEditText)
.withLatestFrom(emailIsValid) { hasFocus, emailIsValid ->
(!hasFocus && !emailIsValid)
}
.subscribe { showError ->
if (showError) {
Log.d("asdf", "error")
}
}
val password = RxTextView.textChanges(passwordEditText)
Observable.combineLatest(emailIsValid, password) { emailIsValid, password ->
Log.d("asdf", "valid: $emailIsValid, password: $password")
(emailIsValid && password.length > 0)
}.subscribe(RxView.enabled(button))
emailIsValid.connect()
或者只是切換subscribe
的順序,因為它會導致問題。
val email = RxTextView.textChanges(emailEditText)
val emailIsValid = email.map { charSequence ->
Log.d("asdf", "emailIsValid call: " + charSequence)
Pattern.matches(Patterns.EMAIL_ADDRESS.pattern(), charSequence)
}.share()
val password = RxTextView.textChanges(passwordEditText)
Observable.combineLatest(emailIsValid, password) { emailIsValid, password ->
Log.d("asdf", "valid: $emailIsValid, password: $password")
(emailIsValid && password.length > 0)
}.subscribe(RxView.enabled(button))
RxView.focusChanges(emailEditText)
.withLatestFrom(emailIsValid) { hasFocus, emailIsValid ->
(!hasFocus && !emailIsValid)
}
.subscribe { showError ->
if (showError) {
Log.d("asdf", "error")
}
}
注意:代碼在Kotlin中,有關發布/連接的更多信息, 請訪問http://www.introtorx.com/content/v1.0.10621.0/14_HotAndColdObservables.html#PublishAndConnect
您的問題類似於PublishAndConnect部分中提到的問題:
第二個訂閱延遲訂閱並錯過了第一個出版物。 我們可以移動Connect()方法的調用,直到完成所有訂閱。 這樣,即使調用Thread.Sleep,在完成兩個訂閱之后我們也不會真正訂閱底層證書。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.