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~8
和4~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
的原理图如下所示:
该操作符接受多个
Observable
以及一个函数作为参数,并且函数的签名为这些Observable
发射的数据类型。当以上的任意一个Observable
发射数据之后,会去取其它Observable
最近一次发射的数据,回调到函数当中,但是该函数回调的前提是所有的Observable
都至少发射过一个数据项。
2.2 combineLatest 和 zip 的区别
zip
和combineLatest
接收的参数格式相同,我们在 RxJava2 实战知识梳理(4) - 结合 Retrofit 请求新闻资讯 中用它来实现等待多个Observable
都发射之后才进行数据的组合,回顾一下它的原理图:
zip
和combineLatest
的区别在于:
-
zip
是在其中一个Observable
发射数据项后,组合所有Observable
最早一个未被组合的数据项,也就是说,组合后的Observable
发射的第n
个数据项,必然是每个源由Observable
各自发射的第n
个数据项构成的。 -
combineLatest
则是在其中一个Observable
发射数据项后,组合所有Observable
所发射的最后一个数据项(前提是所有的Observable
都至少发射过一个数据项)。
更多文章,欢迎访问我的 Android 知识梳理系列:
- Android 知识梳理目录:http://www.jianshu.com/p/fd82d18994ce
- 个人主页:http://lizejun.cn
- 个人知识总结目录:http://lizejun.cn/categories/