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

标签 java android functional-programming rx-java reactive-programming

我正在制作一个简单的登录表单(电子邮件和密码)来尝试增强我的响应式(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 的当前内容作为它的第一项,依次经过 emailIsValidshare继续第一个 Subscriber (即 withLatestFrom 运算符)。

  • 一段时间后 combineLatest导致对 emailIsValid 的另一个订阅.自 emailIsValidshare d,此订阅不会“通过”到email因此每个项目仍然只会被发射一次。

  • 现在的问题是share表现得像 PublishSubject :它只会向所有订阅者发出任何 future 事件,但不会重播任何过去的事件。

总而言之,这意味着:当第二个 Subscriber (combineLatest) 到达,初始值已经过去 - 它是在第一次订阅后立即发出的。下一个值只会在您更改 TextView 的内容时到达.

解决方案:尝试replay(1).refCount()而不是 share()emailIsValid 的末尾- 这应该确保每个新的订阅者也能收到最近的评估结果,以及所有 future 的评估结果。

我希望这能解决问题并且我的解释是有道理的。

关于java - 使用 RxJava 进行电子邮件登录验证,一个 observable 发出两次,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35296903/

相关文章:

java - 检查3个字符串是否位于同一个句子中-Java

java备份mysqldump.exe绝对路径改为相对路径

android - 如何在 Android 5.0 的锁屏或主屏幕上显示小部件/自定义布局

.net - F# 中适合初学者使用的大量资源和业务示例

function - 这个 `init` 的实现是如何工作的?

Java 错误 : Exception in thread "main" java. lang.NoSuchMethodError: main

JAVA布局情况

android - 使用 Sublime Text 2 为 Android 开发 Phonegap 应用程序

Android studio NDK 未定义对 GL 函数的引用

.net - 与可区分联合相比,在 F# 中使用记录类型时更加详细