使用RxJava2取消正在进行的网络呼叫订阅

时间:2018-06-17 19:43:53

标签: android rx-java rx-java2

所以我正在尝试构建一个对话框片段屏幕(Android),其中有一个EditText,用户可以在其中输入所需的用户名。使用RxBindings库的RxTextView.textChanges()我正在观察EditText并执行以下操作:

  1. 重置现有提示并禁用“确认”按钮。
  2. 使用正则表达式模式过滤掉无效字符串。
  3. 向服务器发出请求以检查有效字符串是否可用。
  4. 显示相应的响应,如果适用,启用“确认”按钮。
  5. 我的存储库的checkUsername()方法返回Single<Boolean>以表示用户名是否可用。这是代码:

        Observable<String> usernameObservable = RxTextView.textChanges(usernameEditText)
                .doOnNext(charSequence -> resetUsernameChecks())
                .filter(charSequence -> !TextUtils.isEmpty(charSequence))
                .debounce(400, TimeUnit.MILLISECONDS)
                .map(CharSequence::toString)
                .filter(s -> {
                    boolean match = s.matches("^(?=.{4,12}$)(?![_.])(?!.*[_.]{2})[a-zA-Z0-9._]+(?<![_.])$");
                    if (!match) showMessage(R.string.profile_dialog_username_hint_invalid, true);
                    return match;
                });
    
        Disposable usernameSubscription = usernameObservable
                .subscribe(username -> {
                    Disposable d = viewModel.checkUsername(username)
                            .subscribe(available -> {
                                if (available) {
                                    claimButton.setEnabled(true);
                                    showMessage(R.string.profile_dialog_username_hint_available, false);
                                } else {
                                    showMessage(R.string.profile_dialog_username_hint_unavailable, true);
                                }
                            });
                    disposables.add(d);
                });
    
        disposables.add(usernameSubscription);
    

    使用以下辅助方法:

    @UiThread
    private void resetUsernameChecks() {
        claimButton.setEnabled(false);
        usernameInputLayout.setError(null);
    }
    
    @UiThread
    private void showMessage(@StringRes int message, boolean error) {
        showMessage(getString(message), error);
    }
    
    @UiThread
    private void showMessage(String message, boolean error) {
        if (error) {
            usernameInputLayout.setErrorTextAppearance(R.style.AppTheme_RedMessage);
        } else {
            usernameInputLayout.setErrorTextAppearance(R.style.AppTheme_GreenMessage);
        }
    
        usernameInputLayout.setError(message);
    }
    

    但是,我遇到的问题是 showMessage()方法中的任何一个都会导致崩溃,因为运行中可能存在后台线程(从而阻止更改UI)或者 以前称为checkUsername()的人会匆匆回来并混淆一切。

    基本上,我怎样才能确保我在正确的线程上工作?取消任何延迟的网络呼叫?

    修改: 我通过将UI方法封装在Runnable中来修复线程问题。但是,我仍然有取消之前通话的问题。让我们说用户搜索可用的用户名 - nick - 然后快速点击退格。已经解雇的网络呼叫现在返回并且不准确地显示 nic 可用,即使它可能不是。

1 个答案:

答案 0 :(得分:0)

因此,要更新UI,您必须在UI Thread中执行此操作。我想,

usernameInputLayout.setErrorTextAppearance(R.style.AppTheme_GreenMessage);

就像UI线程一样,所以你需要使用subscribeOn。

如果您要从日志中显示更多内容,我们就会知道错误。但是,现在,您最好的选择是使用UI线程来更新UI。

usernameObservable.subscribeOn(AndroidSchedulers.mainThread())
.subscribe(v -> //whatever you want )

编辑:我的坏事&#34;默认情况下,Observable在后台线程上运行。&#34;这是错误的。正确的是:它在它定义的线程中运行,除非我们使用subscribeOn()/ observeOn()方法。