使用RxJava进行电子邮件登录验证,一个observable发出两次

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:

这解决了问题,但它导致了另一个问题:最后没有初始发出emailIsValidcombineLatest函数,所以Login按钮启动时应该被禁用(灰显).

解决这个问题的最简洁方法是什么?我我希望它表现得像一个BehaviorSubject,但我不确定这是否是最好的方法.

pt2*_*121 1

您可以使用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,我们也不会真正订阅底层,直到两个订阅都完成之后。