5

Tôi đang tạo biểu mẫu đăng nhập đơn giản (email và mật khẩu) để thử và tăng cường kỹ năng lập trình phản ứng của mình. Tôi đang gặp một số vấn đề khi xác thực trường email hoạt động theo cách tôi muốn.Sử dụng RxJava để xác thực đăng nhập email, có thể quan sát được hai lần

Dưới đây là mã của tôi:

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)); 

Và đây là nhật ký:

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:

Như bạn thấy, emailIsValid được gọi là hai lần mỗi khi tôi gõ một ký tự, có nghĩa là nó đang làm một regex phù hợp hai lần, đó là loại lãng phí.

Tôi tra cứu cách tôi có thể thực hiện emailIsValid chỉ gọi một lần cho mỗi thay đổi, bất kể số người đăng ký có và tôi đã tìm thấy phương thức share(). Đây là những gì sẽ xảy ra khi tôi thêm .share() đến hết khai emailIsValid 's:

emailIsValid call: // I type 'j' emailIsValid call: j valid: false, password: // I type 'a' emailIsValid call: ja valid: false, password:

Đó giải quyết vấn đề, nhưng nó gây ra khác: Không có Emit ban đầu bởi emailIsValid đến combineLatest chức năng ở cuối, do đó, nút Đăng nhập bắt đầu được bật, khi nút này sẽ bị tắt (chuyển sang màu xám).

Cách sạch nhất để giải quyết vấn đề này là gì? Tôi nghĩ rằng Tôi muốn nó hoạt động như một số BehaviorSubject, nhưng tôi không chắc đó có phải là cách tốt nhất để làm điều đó hay không.

Trả lời

1

Bạn có thể sử dụng 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() 

Hoặc chỉ chuyển đổi đơn đặt hàng subscribe vì nó gây ra sự cố.

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") 
     } 
    } 

Lưu ý: code đang trong Kotlin và biết thêm về xuất bản/kết nối được tại http://www.introtorx.com/content/v1.0.10621.0/14_HotAndColdObservables.html#PublishAndConnect

Vấn đề của bạn là tương tự như những gì đề cập đến có trong phần PublishAndConnect .:

Thứ hai đăng ký trễ và bỏ lỡ ấn phẩm đầu tiên. Chúng tôi có thể di chuyển lời gọi của phương thức Connect() cho đến sau khi tất cả các đăng ký đã được thực hiện. Bằng cách đó, ngay cả với các cuộc gọi đến Thread.Sleep chúng tôi sẽ không thực sự đăng ký vào cơ bản cho đến sau khi cả hai đăng ký được thực hiện.

+0

Tuyệt vời, không nhận thức được các quan sát có thể kết nối. Hoạt động tốt, cảm ơn bạn! –

+0

@ D_Steve595 NP. Vui vì tôi có thể giúp! – pt2121

1

tôi nghĩ gì đang xảy ra ở đây là như sau:

  • Đầu tiên subscribe - một vào cuối RxView.focusChange()... - gây ra một đăng ký để emailIsValid (và do đó cũng để email).

  • email sau đó sẽ ngay lập tức phát ra các nội dung hiện tại của TextView như mục đầu tiên của nó, mà lần lượt đi qua emailIsValidshare và sang đầu tiên Subscriber (i. E. Các nhà điều hành withLatestFrom).

  • Một thời gian sau, combineLatest làm cho Đăng ký khác thành emailIsValid. Kể từ emailIsValidshare d, Đăng ký này không "chuyển qua" tới email và do đó mỗi mục sẽ vẫn chỉ được phát ra một lần.

  • Vấn đề bây giờ là share cư xử giống như một PublishSubject: Nó chỉ phát ra bất kỳ sự kiện tương lai cho tất cả các thuê bao, nhưng nó không phát lại bất kỳ trong những người trong quá khứ.

Tóm lại điều này có nghĩa: Khi thứ hai Subscriber (các combineLatest) đến, giá trị ban đầu đã qua - nó được phát ra ngay sau khi đăng ký đầu tiên. Và giá trị tiếp theo sẽ chỉ đến khi bạn thay đổi nội dung của số TextView.

Giải pháp: Hãy thửreplay(1).refCount() thay vì share() vào cuối emailIsValid - cần đảm bảo rằng mỗi thuê bao mới cũng nhận được kết quả đánh giá trước đây cuối cùng, cũng như tất cả những người trong tương lai.

Tôi hy vọng rằng giải quyết được vấn đề và giải thích của tôi có ý nghĩa.

Các vấn đề liên quan