您的位置:首页 > 编程语言 > Java开发

Rx_java(6)Rx_java2操作符(debounce 、filter 、switchMap )介绍-搜索功能小案例

2018-01-05 11:52 435 查看

相关文章

观察者模式实例讲解

使用java中的类(Observable与Observer)实现观察者模式

Rx_java2的基本使用

Rx_java2中的线程控制

Rx_java2操作符介绍1(Map、Flatmap)

Rx_java2操作符介绍3(throttleFirst、debounce )

Retrofit结合RxJava2使用

写在前面

本篇将通过一个实例-在输入框输入关键字,从网络获取数据,来讲解Rxjava的几个操作符debouncefilterswitchMap

本案列还将使用Rxbinding2来完成。

搜索的传统方式和弊端

一般来说,我们会监听EditText空间,但数据发生变化后,去请求搜索接口。代码如下:

mEt = (EditText) findViewById(R.id.edittext);

mEt.addTextChangedListener(new TextWatcher() {
@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) {
sea
4000
rch(s.toString().trim());
}
});


当是,上面这种方式会导致以下俩个问题:

可能导致很多没有意义的请求,耗费用户流量(因为控件的值每更改一次立即就会去请求网络,而且只是最后输入的关键字是有用的)

可能导致最终搜索的结果不是用户想要的.

例如,用户一开始输入关键字’AB’ 这个时候出现两个请求, 一个请求是A关键字, 一个请求是AB关键字. 表面上是’A’请求先发出去, ‘AB’请求后发出去. 如果后发出去的’AB’请求先返回, ‘A’请求后返回,那么’A’请求后的结果将会覆盖’AB’请求的结果. 从而导致搜索结果不正确

Rxjava2方式完成搜索功能

我们通过Rxbinding2来绑定空间完成此功能,需要依赖Rxbinding2包

compile 'com.jakewharton.rxbinding2:rxbinding:2.0.0'


因为Rxbinding2中已经依赖了Rxjava2,所以在我们的build.gradle中不需要再次去依赖Rxjava2的包

首先,我们在布局文件中创建一个EditText输入框



然后通过Rxbinding绑定这个控件。

RxTextView.textChanges(mEt)


当我们的EditText中的数据发生变化时,我们需要作出响应,去网络获取数据,按照一般的方式,我们会遇到上面的问题1,耗费用户流量,用无效的关键字去访问网络等问题,

所以,我们这里使用debounce操作符来解决这个问题。

debounce操作符

我们首先看看Rxjava文档对这个操作符介绍(部分,如果要详细看,请自行下载Rxjava中文文档下载



过了一段时间,还没发射数据时,才会发射一个数据,正好,满足我们的需求,不需要频繁的去请求服务器,

RxTextView.textChanges(mEt)
.debounce(200, TimeUnit.MILLISECONDS)


我们用200毫秒所谓一个时间单位去请求网络。需要注意的是,因为此时,我们在操作EditText,所以一定要保证在ui线程

接着 ,我们去请求网络获取数据,往后吧获取到的数据转换为List集合,此时我们需要输入一个字符串,输出一个集合,那么我们想到了flatMap,

所以以下过程就是完整的流程,监听EditText,利用debounce每隔200毫秒,发送关键字数据,请求搜索接口,获取到网络数据,转换为list集合,最终在观察者中获取到

RxTextView.textChanges(mEt)
.debounce(200, TimeUnit.MILLISECONDS)
// 因为是对EditText操作,所以这里必须要放在ui线程
.subscribeOn(AndroidSchedulers.mainThread())

.flatMap(new Function<CharSequence, ObservableSource<List<String>>>() {
@Override
public ObservableSource<List<String>> apply(CharSequence charSequence) throws Exception {
List<String> searchList = search(charSequence);
return Observable.just(searchList);
}
})

// 因为去网络搜索是耗时操作,所以需要切换在子线程中
.subscribeOn(Schedulers.io())
// 搜索结果的将会在ui界面上展示,所以切换到ui线程
.observeOn(AndroidSchedulers.mainThread())
// 绑定观察者接受信息
.subscribe(new Consumer<List<String>>() {
@Override
public void accept(List<String> strings) throws Exception {
// 获取搜索结果
}
}, new Consumer<Throwable>() {
@Override
public void accept(Throwable throwable) throws Exception {
// 在控制台输出错误信息
Log.e("MainActivity",throwable.getMessage());
}
});


此时,我们不仅要问,问题2,前面的请求的数据会覆盖后面的数据这个bug还没解决呢,解决这个问题,我们需要用一个新的操作符switchMap

switchMap操作符

先看看switchMap的文档



它和flatMap很像,即说明它和flatMap具有同样的功能-数据转换,但是它比起flatMap不同的是,当有新的数据时,它将取消并停止监听之前产生的数据,只监视但前的。正好,针对我们的问题,我们之前的问题是,由于网络延迟等原因,前面的数据覆盖后了后面的数据。现在,我们需要用switchMap替换掉flatMap即可,注意,因为他们的实现方式完全一样,所以我们只需要把flatMap这个函数名改为switchMap即可。

 RxTextView.textChanges(mEt)
.debounce(200, TimeUnit.MILLISECONDS)
// 因为是对EditText操作,所以这里必须要放在ui线程
.subscribeOn(AndroidSchedulers.mainThread())

// 从网络获取搜索字符串数据,switchMap比较flatMap,因为switchMap会发送最近的数据,
.switchMap(new Function<CharSequence, ObservableSource<List<String>>>() {
@Override
public ObservableSource<List<String>> apply(CharSequence charSequence) throws Exception {
List<String> searchList = search(charSequence);
return Observable.just(searchList);
}
})

// 因为去网络搜索是耗时操作,所以需要切换在子线程中
.subscribeOn(Schedulers.io())
// 搜索结果的将会在ui界面上展示,所以切换到ui线程
.observeOn(AndroidSchedulers.mainThread())
// 绑定观察者接受信息
.subscribe(new Consumer<List<String>>() {
@Override
public void accept(List<String> strings) throws Exception {
// 获取搜索结果
}
}, new Consumer<Throwable>() {
@Override
public void accept(Throwable throwable) throws Exception {
// 在控制台输出错误信息
Log.e("MainActivity",throwable.getMessage());
}
});


但是,此时,如果输入框中字符串发生了变化,即我们点击了退格,把数据清空,空的字符串也会去请求网络,那么,我们该怎么解决呢,需要用另一个操作符filter

filter操作符

同样,先看文旦



filter完成过滤操作,这个操作符很好理解,在它的Predicate方法,如果返回为true,就是可以通过的数据,反之,就需要被拦截,当然,这个拦截规则需要我们自己去实现。

在我们当前这个项目中,我们需呀过滤掉空的字符串,所以我们只要当字符串为空,返回false,否则返回true即可。最终,完整的代码为:

 RxTextView.textChanges(mEt)
.debounce(200, TimeUnit.MILLISECONDS)
// 因为是对EditText操作,所以这里必须要放在ui线程
.subscribeOn(AndroidSchedulers.mainThread())
// 进行过滤操作,只有字符串长度大于0,才能继续
.filter(new Predicate<CharSequence>() {
@Override
public boolean test(CharSequence charSequence) throws Exception {
if(charSequence.toString().trim().length() > 0){
return true;
} else {
return false;
}
}
})

// 从网络获取搜索字符串数据,switchMap比较flatMap,因为switchMap会发送最近的数据,
.switchMap(new Function<CharSequence, ObservableSource<List<String>>>() {
@Override
public ObservableSource<List<String>> apply(CharSequence charSequence) throws Exception {
List<String> searchList = search(charSequence);
return Observable.just(searchList);
}
})

// 因为去网络搜索是耗时操作,所以需要切换在子线程中
.subscribeOn(Schedulers.io())
// 搜索结果的将会在ui界面上展示,所以切换到ui线程
.observeOn(AndroidSchedulers.mainThread())
// 绑定观察者接受信息
.subscribe(new Consumer<List<String>>() {
@Override
public void accept(List<String> strings) throws Exception {
// 获取搜索结果
}
}, new Consumer<Throwable>() {
@Override
public void accept(Throwable throwable) throws Exception {
// 在控制台输出错误信息
Log.e("MainActivity",throwable.getMessage());
}
});


结果演示



通过上面的演示结果,我们看到,但很快的输入abc的时候,不会去联网查询三次,只是查询一次,所以有效避免了那些没有意义的请求,当我们清空数据时,也不会再去查询。

写在后面

本篇通过一个项目中常用的搜索功能来介绍了三个新的操作符debouncefilterswitchMap。而且还使用了Rxbinding2,关于Rxbinding2,将会令写一篇文章介绍。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: