RxJava(七) 使用 debounce 操作符优化 App 搜索功能[通俗易懂]

RxJava(七) 使用 debounce 操作符优化 App 搜索功能[通俗易懂]RxJava系列文章目录导读:一、RxJavacreate操作符的用法和源码分析二、RxJavamap操作符用法详解三、RxJavaflatMap操作符用法详解四、RxJavaconcatMap操作符用法详解五、RxJavaonErrorResumeNext操作符实现app与服务器间token机制六、RxJavaretryWhen操作符…

大家好,又见面了,我是你们的朋友全栈君。

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 操作符实现获取验证码功能
十五、RxJava 线程的自由切换


一、抛出问题

现在几乎所有的 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 就可以了。


如果你觉得本文帮助到你,给我个关注和赞呗!

另外,我为 Android 程序员编写了一份:超详细的 Android 程序员所需要的技术栈思维导图

如果有需要可以移步我的 GitHub -> AndroidAll,里面包含了最全的目录和对应知识点链接,帮你扫除 Android 知识点盲区。 由于篇幅原因只展示了 Android 思维导图:
超详细的Android技术栈

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/151576.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • mysql 数据库字符串转时间_mysql时间与字符串之间相互转换详解

    mysql 数据库字符串转时间_mysql时间与字符串之间相互转换详解1.时间转字符串DATE_FORMAT(日期,格式字符串)SELECTDATE_FORMAT(NOW(),’%Y-%m-%d%H:%i:%s’);2.字符串转时间STR_TO_DATE(字符串,日志格式)SELECTSTR_TO_DATE(‘2019-01-2016:01:45′,’%Y-%m-%d%H:%i:%s’);3.时间转时间戳selectunix_timestamp(no…

  • thread 守护线程_守护线程与非守护线程

    thread 守护线程_守护线程与非守护线程  Java中线程分为用户线程(userthread)和守护线程(daemonthread),它们通过Thread的daemon属性标识:true表示守护线程,false表示用户线程。  一个Thread初始默认为用户线程(daemon默认为false),创建Thread的时候默认从当前线程”继承”daemon属性,见Thread.init方法:Threadparent=currentThread();this.daemon=parent.isDaemon();  当虚拟机中剩余运行

    2022年10月15日
  • 详解C语言中的数组指针与指针数组

    详解C语言中的数组指针与指针数组·详解数组指针与指针数组·数组指针一、区分首先我们需要了解什么是数组指针以及什么是指针数组,如下图:int*p[5];int(*p)[5];数组指针的意思即为通过指针引用数组,p先和*结合,说明了p是一个指针变量,指向一个大小为5的数组。所以,int(*p)[5]即为一个数组指针。int*p[5]则是一个大小为5且存放整型指针的数组。二、数组元素的指针1.定…

  • linux 查看文件内容 显示行号

    linux 查看文件内容 显示行号怎么在linux系统中查看文件时显示行号?1.使用vi或者vim命令打开文件打开后的文件内容日如下2.直接输入以下命令,按Ente健显示文件行号:setnu或者:setnumber成功后显示如下…

  • Mac 卸载cuda

    Mac 卸载cudaMac卸载cudasudoperl/usr/local/bin/uninstall_cuda_drv.pl检查是否卸载成功:gcc-v

  • pycharm中使用anaconda部署python环境_pycharm怎么用anaconda的环境

    pycharm中使用anaconda部署python环境_pycharm怎么用anaconda的环境每一种语言的开发环境都是包含了运行环境和开源包两个核心内容。比如Java,JDK是运行环境,而开发导入需要用到的各种第三方工具都是以开源包的形式导入的。再比如Python,python3.6/python2.7是它的运行环境,而pynum,pandas这些数据处理工具就是也是开源包。通常情况下,我们都是使用IDE在项目中统一管理运行环境和开源包。比如开发JavaWeb项目我们使用Myec…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号