場景
在進(jìn)行搜索功能開發(fā)(特別是需要從網(wǎng)絡(luò)或者本地加載)的時候幔崖,為了給用戶以更好體驗:
比如用戶想要搜索“abc”,如果每次輸入的文字變化都執(zhí)行一次請求(確實很垃圾)坑雅,那么就會陸續(xù)搜索“a”,“ab”,“abc”。這還是在搜索比較少的情況下,如果搜索字?jǐn)?shù)較多,又或者網(wǎng)絡(luò)狀況不好学赛,那么用戶的體驗一定會很差,所以節(jié)流就很有必要性吞杭。通常的做法便是:
設(shè)置一個延遲時間盏浇,過濾掉變化過快的字符
而實現(xiàn)的方式,我總結(jié)了有以下三種:
- Handler+Thread
- Executor+Future
- RxJava
apk地址:Demo
實現(xiàn)(延遲500ms搜索)
1.Handler+Thread
第一種方式使用我們都很熟悉的Handler,配合Hanlder的removeCallbacks
方法芽狗,或者removeMessages
方法移除Callback/Messages缠捌。延遲的話使用postDelayed
方法就可以實現(xiàn)。
@Override
public boolean onQueryTextChange(String newText) {
if (!TextUtils.isEmpty(newText)) {
queryWithHandler(newText);
}
return true;
}
private void queryWithHandler(String newText) {
// 延遲
if (delayQueryTask != null) {
delayQueryTask.cancel();
handler.removeCallbacksAndMessages(null);
}
delayQueryTask = new DelayQueryRunnable(newText);
handler.postDelayed(delayQueryTask, 500);
}
private class DelayQueryRunnable implements Runnable {
String mText;
private boolean canceled = false;
public DelayQueryRunnable(String text) {
this.mText = text;
}
@Override
public void run() {
if (canceled) {
return;
}
queryMatch(mText);
}
public void cancel() {
canceled = true;
}
}
2.Executor+Future
Future模式可以這樣來描述:我有一個任務(wù),提交給了Future曼月,F(xiàn)uture替我完成這個任務(wù)谊却。期間我自己可以去做任何想做的事情。一段時間之后哑芹,我就便可以從Future那兒取出結(jié)果
在onQueryTextChange函數(shù)即輸入框內(nèi)容每次變化時將一個數(shù)據(jù)獲取線程SearchThread
放到scheduledExecutor
中炎辨,如果當(dāng)前正在執(zhí)行搜索,那么取消這個任務(wù)重新搜索聪姿,避免不必要的數(shù)據(jù)獲取和多個搜索提示同時出現(xiàn)碴萧。
@Override
public boolean onQueryTextChange(String newText) {
if (!TextUtils.isEmpty(newText)) {
showSearchTip(newText);
}
return true;
}
private MyHandler mHandler ;
private Future<?> mFuture;
// 創(chuàng)建 SingleThreadExecutor
ScheduledExecutorService scheduledExecutor = Executors.newSingleThreadScheduledExecutor();
public ScheduledFuture<?> schedule(Runnable command, long delayTimeMills) {
return scheduledExecutor.schedule(command, delayTimeMills, TimeUnit.MILLISECONDS);
}
public void showSearchTip(String newText) {
if (mFuture!=null){
//取消任務(wù),true代表立即取消
mFuture.cancel(true);
}
// 延遲500毫秒
mFuture=schedule(new SearchThread(newText), 500);
}
class SearchThread implements Runnable {
String newText;
public SearchThread(String newText) {
this.newText = newText;
}
public void run() {
if (!TextUtils.isEmpty(newText)) {
mHandler.sendMessage(mHandler.obtainMessage(1, newText ));
}
}
}
private class MyHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 1:
queryMatch((String) msg.obj);
break;
}
}
}
3.RxJava
使用rxjava最簡單末购,使用PublishSubject和Debounce操作符很容易實現(xiàn)延遲搜索破喻。PublishSubject
既是發(fā)送者也是接收者,可以用于接收變化的字符串盟榴,而Debounce
操作符會過濾掉發(fā)射速率過快的數(shù)據(jù)項曹质,更多優(yōu)化可以看這一篇使用RxJava 提升用戶體驗。
private PublishSubject<String > mSubject=PublishSubject.create();
private void initData() {
//其他操作
mSubject.debounce(500, TimeUnit.MILLISECONDS)//延遲500ms
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnNext(new Action1<String>() {
@Override
public void call(String s) {
//從網(wǎng)絡(luò)或者本地搜索
queryMatch(s);
}
}).subscribe();
}
@Override
public boolean onQueryTextChange(String newText) {
if (!TextUtils.isEmpty(newText)) {
queryWithRxJava(newText);
}
return true;
}
private void queryWithRxJava(String newText) {
//發(fā)送數(shù)據(jù)源
mSubject.onNext(newText);
}
結(jié)語
三種方法介紹完畢擎场,也許可以做些優(yōu)化羽德,使用HandlerThread
什么的,或者還有其他更好的方法迅办,歡迎交流宅静。