簡體   English   中英

使用RxJava進行電子郵件登錄驗證,一個observable發出兩次

[英]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沒有初始發出到emailIsValidcombineLatest函數,因此Login按鈕啟動時應該被禁用(灰顯)。

解決這個問題的最簡潔方法是什么? 我希望它表現得像一個BehaviorSubject ,但我不確定這是否是最好的方法。

這里發生的事情如下:

  • 第一個subscribe - 在RxView.focusChange()...結尾處的訂閱 - 導致訂閱到emailIsValid (因此也emailemail )。

  • 然后, email將立即將TextView的當前內容作為其第一項發出,然后通過emailIsValid進行shareshare給第一個Subscriber (即withLatestFrom運算符)。

  • 一段時間后, combineLatest導致另一個訂閱到emailIsValid 由於emailIsValidshare d,因此此訂閱不會“通過”到email ,因此每個項目仍將只發出一次。

  • 現在的問題是, share行為類似於PublishSubject :它只是向所有訂閱者發出任何未來事件,但它不會重放任何過去的事件。

總之,這意味着:當第二個SubscribercombineLatest )到達時,初始值已經過去 - 它是在第一個訂閱之后立即發出的。 並且只有在更改 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM