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

###相关文章

搜索的传统方式和弊端

一般来说,我们会监听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) {
                search(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,将会令写一篇文章介绍。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值