Ste*_*oen 6 java android functional-programming reactive-programming rx-java
我正在制作一个简单的登录表单(电子邮件和密码)来尝试加强我的反应式编程技能组合.我在使电子邮件字段验证按照我想要的方式工作时遇到了一些麻烦.
这是我的代码:
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));
Run Code Online (Sandbox Code Playgroud)
这是日志:
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的combineLatest函数,所以Login按钮启动时应该被禁用(灰显).
解决这个问题的最简洁方法是什么?我想我希望它表现得像一个BehaviorSubject,但我不确定这是否是最好的方法.
您可以使用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()
Run Code Online (Sandbox Code Playgroud)
或者只是改变你的顺序,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")
}
}
Run Code Online (Sandbox Code Playgroud)
注意:代码采用 Kotlin 语言,有关发布/连接的更多信息位于http://www.introtorx.com/content/v1.0.10621.0/14_HotAndColdObservables.html#PublishAndConnect
您的问题与 PublishAndConnect 部分中提到的问题类似:
第二次订阅订阅较晚,错过了第一次发布。我们可以将 Connect() 方法的调用移至所有订阅完成之后。这样,即使调用 Thread.Sleep,我们也不会真正订阅底层,直到两个订阅都完成之后。