RxJava(七) 使用debounce操作符 优化app搜索功能

原创 2016年06月01日 13:16:26

欢迎转载,转载请标明出处:
http://blog.csdn.net/johnny901114/article/details/51555203
本文出自:【余志强的博客】

RxJava系列文章目录导读:

一、RxJava create操作符的用法和源码分析
二、RxJava map操作符用法详解
三、RxJava flatMap操作符用法详解
四、RxJava concatMap操作符用法详解
五、RxJava onErrorResumeNext操作符实现app与服务器间token机制
六、RxJava retryWhen操作符实现错误重试机制
七、RxJava 使用debounce操作符优化app搜索功能
八、RxJava concat操作处理多数据源
九、RxJava zip操作符在Android中的实际使用场景
十、RxJava switchIfEmpty操作符实现Android检查本地缓存逻辑判断
十一、RxJava defer操作符实现代码支持链式调用
十二、combineLatest操作符的高级使用
十三、RxJava导致Fragment Activity内存泄漏问题
十四、interval、takeWhile操作符实现获取验证码功能


一、抛出问题

现在几乎所有的App都有搜索功能 , 一般情况我们监听EditText控件,当值发生改变去请求搜索接口. 如:

etKey.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) {
        String key = etKey.getText().toString().trim();
        if (key.length() > 0){
            search(key);// 请求搜索接口,成功后把结果显示到界面上.
        }
    }
});

这样做有两个问题:

  • 可能导致很多没有意义的请求,耗费用户流量(因为控件的值每更改一次立即就会去请求网络,而且只是最后输入的关键字是有用的)
  • 可能导致最终搜索的结果不是用户想要的. 例如,用户一开始输入关键字’AB’ 这个时候出现两个请求, 一个请求是A关键字, 一个请求是AB关键字. 表面上是’A’请求先发出去, ‘AB’请求后发出去. 如果后发出去的’AB’请求先返回, ‘A’请求后返回,那么’A’请求后的结果将会覆盖’AB’请求的结果. 从而导致搜索结果不正确.

二、如何解决问题

使用强大的RxJava的 debounce操作符 可以解决这个问题。

subscription = RxTextView.textChanges(etKey)
                .debounce(400, TimeUnit.MILLISECONDS, AndroidSchedulers.mainThread())
                .subscribeOn(AndroidSchedulers.mainThread())// 对etKey[EditText]的监听操作 需要在主线程操作
                //对用户输入的关键字进行过滤
                .filter(new Func1<CharSequence, Boolean>() {
                    @Override
                    public Boolean call(CharSequence charSequence) {
                        Log.d("RxJava", "filter is main thread : " + (Looper.getMainLooper() == Looper.myLooper()));
                        return charSequence.toString().trim().length() > 0;
                    }
                })
                .flatMap(new Func1<CharSequence, Observable<List<String>>>() {
                    @Override
                    public Observable<List<String>> call(CharSequence charSequence) {
                        Log.d("RxJava", getMainText("flatMap"));
                        return searchApi.search(charSequence.toString());
                    }
                })
                //.subscribeOn(Schedulers.io()不起作用
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Action1<List<String>>() {
                    @Override
                    public void call(List<String> strings) {
                        tvContent.setText("search result:\n\n");
                        tvContent.append(strings.toString());
                    }
                }, new Action1<Throwable>() {
                    @Override
                    public void call(Throwable throwable) {
                        throwable.printStackTrace();
                        tvContent.append("Error:" + throwable.getMessage());
                    }
                });

上面代码的主要逻辑:

  • 使用debounce操作符设置: 只有当用户输入关键字后400毫秒才发射数据[说的直白点就是400毫秒后才会走后面的逻辑];
  • 使用filter操作符 对用户输入的关键字进行过滤:只有输入的关键字不为空,才会走后面的逻辑;
  • 使用flatMap操作符:使用最终的关键字去请求搜索接口

至此,避免EditText每改变一次就请求一次的情况。
但是,还有一个问题,上面说的导致搜索结果的错乱,上面的代码还是没有解决,比如停止输入400毫秒后, 那么肯定会开始请求Search接口, 但是用户又会输入新的关键字,这个时候上个请求还没有返回, 新的请求又去请求Search接口.这个时候有可能最后的一个请求返回, 第一个请求最后返回,导致最终显示的结果是第一次搜索的结果.

怎么去解决这个问题:可以使用switchMap操作符解决。

看看官网对 switchMap操作符 如何解释的:

Returns a new Observable by applying a function that you supply to each item emitted by the source Observable that returns an Observable, 
and then emitting the items emitted by the most recently emitted of these Observables.

switchMap操作符flatMap操作符 差不多,区别是switchMap操作符只会发射[emit]最近的Observables。

也就是说,当400毫秒后,发出第一个搜索请求,当这个请求的过程中,用户又去搜索了,发出第二个请求,不管怎样,switchMap操作符只会发射第二次请求的Observable。所以,在上面的代码基础上把flatMap改成switchMap就可以了。

三、线程切换

上面的filter、flatMap、switchMap在主线程执行,如果想其在子线程中执行怎么办,可以把debounce(400, TimeUnit.MILLISECONDS, AndroidSchedulers.mainThread())
改成
debounce(400, TimeUnit.MILLISECONDS, Schedulers.io())即可。但是如果我想精确控制怎么办?例如filter在主线程中执行,flatMap在子线程中执行。对于线程切换的问题,后面再开一篇博客来讲!


本文的例子放在github上 https://github.com/chiclaim/android-sample/tree/master/rxjava

版权声明:本文为博主原创文章,未经博主允许不得转载。

RxJava过滤操作符 debounce

debounce操作符是对源Observable间隔期产生的结果进行过滤,如果在这个规定的间隔期内没有别的结果产生,则将这个结果提交给订阅者,否则忽略该结果,原理有点像光学防抖. .debounce...
  • axuanqq
  • axuanqq
  • 2016年02月29日 10:06
  • 1666

可能是东半球最全的RxJava使用场景小结

知道RxJava吗?这里罗列了东半球最全的RxJava使用场景哦,快来看看吧~
  • THEONE10211024
  • THEONE10211024
  • 2015年12月30日 16:31
  • 48753

APP实用开发—RxJava 和 Retrofit 结合使用

RxJava 和 Retrofit 结合使用的几个最常见使用方式举例。RxJava中有个叫做Subscription的接口,可以用来取消订阅.public interface Subscription...
  • qq_37293612
  • qq_37293612
  • 2017年02月10日 22:34
  • 330

RxJava与Retrofit实战总结

RxAndroid模块包含RxJava的Android特定的绑定代码。它给RxJava添加了一些类,用于帮助在Android应用中编写响应式(reactive)的组件。它提供了一个可以在给定的And...
  • duanyy1990
  • duanyy1990
  • 2016年08月21日 20:49
  • 2883

RxJava中的不好理解的点-join、Debounce or Sample操作符

接触了RxJava一段时间后,对一些operation的理解还是不到位,这里分析总结下。Debounce or Sampledebounce的原意大致是“去抖动; 防抖动; 弹跳”,sample的意思...
  • jdsjlzx
  • jdsjlzx
  • 2016年11月13日 22:38
  • 1426

RxJava过滤操作符

概述过滤操作符用于过滤和选择Observable发射的数据序列,让Observable只返回满足我们条件的数据。DebounceDebounce会过滤掉发射速率过快的数据项,相当于限流,但是需要注意的...
  • wbwjx
  • wbwjx
  • 2016年04月30日 19:05
  • 2545

RxJava操作符 debounce 和 distinctUntilChanged

1 debounce操作符是用来防重复数据的或者(防抖动): public final Observable debounce(long timeout, TimeUnit unit) { ...
  • Pizza_Lawson
  • Pizza_Lawson
  • 2016年05月23日 15:28
  • 1858

RxJava之过滤操作符

涉及到列表的数据时,总是会想到一个过滤这个词语。比如,在1-100的整数中,筛选出偶数或者奇数相加,或者将前49个数相加,又或者后36个数相加,等等。在这样的场景中,不由想到将需要的数据筛选出来。在发...
  • IO_Field
  • IO_Field
  • 2016年05月11日 22:54
  • 9416

RxJava 使用debounce操作符 优化app搜索功能

问题现在几乎所有的App都有搜索功能 , 一般情况我们监听EditText控件,当值发生改变去请求搜索接口. 如:etKey.addTextChangedListener(new TextWatche...
  • jdsjlzx
  • jdsjlzx
  • 2016年06月21日 21:01
  • 5094

[Android开发] RxJava2之路五 - 过滤操作符例子Demo

一、过滤操作符列表用于过滤和选择Observable发射的数据序列 方法 含义 filter() 过滤数据 takeLast() 只发射最后的N项数据 last() 只发...
  • niubitianping
  • niubitianping
  • 2017年02月20日 09:09
  • 2137
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:RxJava(七) 使用debounce操作符 优化app搜索功能
举报原因:
原因补充:

(最多只允许输入30个字)