美文网首页Android 技术开发RxJavaAndroid Rxjava
RxJava2 实战知识梳理(7) - 基于 combineLa

RxJava2 实战知识梳理(7) - 基于 combineLa

作者: 泽毛 | 来源:发表于2017-08-30 00:26 被阅读1941次

    RxJava2 实战系列文章

    RxJava2 实战知识梳理(1) - 后台执行耗时操作,实时通知 UI 更新
    RxJava2 实战知识梳理(2) - 计算一段时间内数据的平均值
    RxJava2 实战知识梳理(3) - 优化搜索联想功能
    RxJava2 实战知识梳理(4) - 结合 Retrofit 请求新闻资讯
    RxJava2 实战知识梳理(5) - 简单及进阶的轮询操作
    RxJava2 实战知识梳理(6) - 基于错误类型的重试请求
    RxJava2 实战知识梳理(7) - 基于 combineLatest 实现的输入表单验证
    RxJava2 实战知识梳理(8) - 使用 publish + merge 优化先加载缓存,再读取网络数据的请求过程
    RxJava2 实战知识梳理(9) - 使用 timer/interval/delay 实现任务调度
    RxJava2 实战知识梳理(10) - 屏幕旋转导致 Activity 重建时恢复任务
    RxJava2 实战知识梳理(11) - 检测网络状态并自动重试请求
    RxJava2 实战知识梳理(12) - 实战讲解 publish & replay & share & refCount & autoConnect
    RxJava2 实战知识梳理(13) - 如何使得错误发生时不自动停止订阅关系
    RxJava2 实战知识梳理(14) - 在 token 过期时,刷新过期 token 并重新发起请求
    RxJava2 实战知识梳理(15) - 实现一个简单的 MVP + RxJava + Retrofit 应用


    一、前言

    2.1 应用背景

    在平时的应用中,我们经常会让用户输入一些信息,最常见的莫过于注册或者登录界面中,让用户输入用户名或者密码,但是我们经常会对用户名或者密码有一定的要求,只有当它们同时满足要求时,才允许用户进行下一步的操作。

    这个需求就涉及到一种模型,即在多个地方监听变化,但是在一个地方进行统一验证,如果验证成功,那么允许用户进行下一步的操作,否则提示用户输入不正确。

    通过这个例子,大家将学习到combineLatest操作符的用法。

    2.2 示例

    在下面这个示例中,包含了两个输入框,分别对应用户名和密码,它们的长度要求分别为2~84~16,如果两者都正确,那么登录按钮的文案变为“登录”,否则显示“用户名或密码无效”。

    public class CombineLatestActivity extends AppCompatActivity {
    
        private EditText mEtName;
        private EditText mEtPassword;
        private Button mBtLogin;
        private PublishSubject<String> mNameSubject;
        private PublishSubject<String> mPasswordSubject;
        private CompositeDisposable mCompositeDisposable;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_combine_latest);
            mEtName = (EditText) findViewById(R.id.et_name);
            mEtPassword = (EditText) findViewById(R.id.et_password);
            mBtLogin = (Button) findViewById(R.id.bt_login);
            mNameSubject = PublishSubject.create();
            mPasswordSubject = PublishSubject.create();
            mEtName.addTextChangedListener(new EditTextMonitor(mNameSubject));
            mEtPassword.addTextChangedListener(new EditTextMonitor(mPasswordSubject));
            Observable<Boolean> observable = Observable.combineLatest(mNameSubject, mPasswordSubject, new BiFunction<String, String, Boolean>() {
    
                @Override
                public Boolean apply(String name, String password) throws Exception {
                    int nameLen = name.length();
                    int passwordLen = password.length();
                    return nameLen >= 2 && nameLen <= 8 && passwordLen >= 4 && passwordLen <= 16;
                }
    
            });
            DisposableObserver<Boolean> disposable = new DisposableObserver<Boolean>() {
    
                @Override
                public void onNext(Boolean value) {
                    mBtLogin.setText(value ? "登录" : "用户名或密码无效");
                }
    
                @Override
                public void onError(Throwable e) {
    
                }
    
                @Override
                public void onComplete() {
    
                }
    
            };
            observable.subscribe(disposable);
            mCompositeDisposable = new CompositeDisposable();
            mCompositeDisposable.add(disposable);
        }
    
        private class EditTextMonitor implements TextWatcher {
    
            private PublishSubject<String> mPublishSubject;
    
            EditTextMonitor(PublishSubject<String> publishSubject) {
                mPublishSubject = publishSubject;
            }
    
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
    
            }
    
            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {
    
            }
    
            @Override
            public void afterTextChanged(Editable s) {
                mPublishSubject.onNext(s.toString());
            }
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            mCompositeDisposable.clear();
        }
    }
    

    在上面的例子中,我们首先创建了两个PublishSubject,分别用于用户名和密码的订阅,然后通过combineLatest对这两个PublishSubject进行组合。这样,当任意一个PublishSubject发送事件之后,就会回调combineLatest最后一个函数的apply方法,该方法会取到每个被观察的PublishSubject最后一次发射的数据,我们通过该数据进行验证。

    最终运行的效果为:


    二、示例解析

    2.1 combineLatest 原理

    combineLatest的原理图如下所示:

    combineLatest 原理图
    该操作符接受多个Observable以及一个函数作为参数,并且函数的签名为这些Observable发射的数据类型。当以上的任意一个Observable发射数据之后,会去取其它Observable 最近一次发射的数据,回调到函数当中,但是该函数回调的前提是所有的Observable至少发射过一个数据项

    2.2 combineLatest 和 zip 的区别

    zipcombineLatest接收的参数格式相同,我们在 RxJava2 实战知识梳理(4) - 结合 Retrofit 请求新闻资讯 中用它来实现等待多个Observable都发射之后才进行数据的组合,回顾一下它的原理图:

    zip 原理图
    zipcombineLatest的区别在于:
    • zip是在其中一个Observable发射数据项后,组合所有Observable最早一个未被组合的数据项,也就是说,组合后的Observable发射的第n个数据项,必然是每个源由Observable各自发射的第n个数据项构成的。
    • combineLatest则是在其中一个Observable发射数据项后,组合所有Observable所发射的最后一个数据项(前提是所有的Observable都至少发射过一个数据项)。

    更多文章,欢迎访问我的 Android 知识梳理系列:

    相关文章

      网友评论

      • Silence潇湘夜雨:我觉得应该是那样的,你这里关于zip和combineLatest的区别应该是,zip在同步的时候等待所有的数据发送完成才组合然后交给下游处理。异步的时候是多个observable都各自发送了一个之后然后组合处理,并且以数据最少的那个obserable为准。combineLatest如果需要组合必须是多个observable必须要有一个发射的数据源,这样才可以处理。

      本文标题:RxJava2 实战知识梳理(7) - 基于 combineLa

      本文链接:https://www.haomeiwen.com/subject/hytzdxtx.html