我正在制作一个简单的登录表单(电子邮件和密码)来尝试增强我的响应式(Reactive)编程技能。我在让电子邮件字段验证按我想要的方式工作时遇到了一些问题。
这是我的代码:
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 调用:
emailIsValid 调用:
有效:假,密码:
//我输入 'j'
emailIsValid 调用:j
emailIsValid 调用:j
有效:假,密码:
//我输入 'a'
emailIsValid 调用:ja
emailIsValid 调用:ja
有效:false,密码:
如您所见,每次我键入一个字符时,emailIsValid
都会被调用两次,这意味着它会进行两次正则表达式匹配,这有点浪费。
我查看了如何使 emailIsValid
每次更改只调用一次,无论它有多少订阅者,我找到了 share()
方法。以下是当我将 .share()
添加到 emailIsValid
声明的末尾时发生的情况:
emailIsValid 调用:
//我输入 'j'
emailIsValid 调用:j
有效:假,密码:
//我输入 'a'
emailIsValid 调用:ja
有效:false,密码:
这解决了问题,但它导致了另一个问题:emailIsValid
没有初始发送到最后的 combineLatest
函数,因此登录按钮开始启用,当它应该被禁用(变灰)。
解决这个问题最干净的方法是什么?我认为我希望它表现得像一个BehaviorSubject
,但我不确定这是否是最好的方式。
最佳答案
我认为这里发生的事情如下:
第一个
subscribe
-RxView.focusChange()...
末尾的那个- 导致订阅emailIsValid
(因此也是email
)。email
然后将立即发出TextView
的当前内容作为它的第一项,依次经过emailIsValid
和share
继续第一个Subscriber
(即withLatestFrom
运算符)。一段时间后
combineLatest
导致对emailIsValid
的另一个订阅.自emailIsValid
是share
d,此订阅不会“通过”到email
因此每个项目仍然只会被发射一次。现在的问题是
share
表现得像PublishSubject
:它只会向所有订阅者发出任何 future 事件,但不会重播任何过去的事件。
总而言之,这意味着:当第二个 Subscriber
(combineLatest
) 到达,初始值已经过去 - 它是在第一次订阅后立即发出的。下一个值只会在您更改 TextView
的内容时到达.
解决方案:尝试replay(1).refCount()
而不是 share()
在 emailIsValid
的末尾- 这应该确保每个新的订阅者也能收到最近的评估结果,以及所有 future 的评估结果。
我希望这能解决问题并且我的解释是有道理的。
关于java - 使用 RxJava 进行电子邮件登录验证,一个 observable 发出两次,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35296903/