
正文
RxJava(七) 使用debounce操作符 优化app搜索功能
提示:扫一扫查出行【扫一扫了解最新限行尾号】
复制提示
欢迎转载,转载请标明出处:
http://blog.csdn.net/johnny901114/article/details/51555203
本文出自:【余志强的博客】
一、抛出问题
现在几乎所有的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就可以了。
github源码下载








