幾乎每一個應用程序都提供了搜索功能,某些應用還提供了搜索聯想。對於一個搜索聯想功能,最基本的實現流程爲:客戶端經過EditText
的addTextChangedListener
方法監聽輸入框的變化,當輸入框發生變化以後就會回調afterTextChanged
方法,客戶端利用當前輸入框內的文字向服務器發起請求,服務器返回與該搜索文字關聯的結果給客戶端進行展現。java
在該場景下,有幾個能夠優化的方面:服務器
abc
,那麼按照上面的實現,客戶端就會發起a
、ab
、abc
三個請求。ab
和abc
,那麼首先會發起關鍵詞爲ab
請求,以後再發起abc
的請求,可是abc
的請求若是先於ab
的請求返回,那麼就會形成用戶指望搜索的結果爲abc
,最終展示的結果倒是和ab
關聯的。這裏,咱們針對上面提到的三個問題,使用RxJava2
提供的三個操做符進行了優化:app
debounce
操做符,當輸入框發生變化時,不會馬上將事件發送給下游,而是等待200ms
,若是在這段事件內,輸入框沒有發生變化,那麼才發送該事件;反之,則在收到新的關鍵詞後,繼續等待200ms
。filter
操做符,只有關鍵詞的長度大於0
時才發送事件給下游。switchMap
操做符,這樣當發起了abc
的請求以後,即便ab
的結果返回了,也不會發送給下游,從而避免了出現前面介紹的搜索詞和聯想結果不匹配的問題。public class SearchActivity extends AppCompatActivity {
private EditText mEtSearch;
private TextView mTvSearch;
private PublishSubject<String> mPublishSubject;
private DisposableObserver<String> DisposableObserver;
private CompositeDisposable mCompositeDisposable;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_search);
mEtSearch = (EditText) findViewById(R.id.et_search);
mTvSearch = (TextView) findViewById(R.id.tv_search_result);
mEtSearch.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) {
startSearch(s.toString());
}
});
mPublishSubject = PublishSubject.create();
DisposableObserver = new DisposableObserver<String>() {
@Override
public void onNext(String s) {
mTvSearch.setText(s);
}
@Override
public void onError(Throwable throwable) {
}
@Override
public void onComplete() {
}
};
mPublishSubject.debounce(200, TimeUnit.MILLISECONDS).filter(new Predicate<String>() {
@Override
public boolean test(String s) throws Exception {
return s.length() > 0;
}
}).switchMap(new Function<String, ObservableSource<String>>() {
@Override
public ObservableSource<String> apply(String query) throws Exception {
return getSearchObservable(query);
}
}).observeOn(AndroidSchedulers.mainThread()).subscribe(DisposableObserver);
mCompositeDisposable = new CompositeDisposable();
mCompositeDisposable.add(mCompositeDisposable);
}
private void startSearch(String query) {
mPublishSubject.onNext(query);
}
private Observable<String> getSearchObservable(final String query) {
return Observable.create(new ObservableOnSubscribe<String>() {
@Override
public void subscribe(ObservableEmitter<String> observableEmitter) throws Exception {
Log.d("SearchActivity", "開始請求,關鍵詞爲:" + query);
try {
Thread.sleep(100 + (long) (Math.random() * 500));
} catch (InterruptedException e) {
if (!observableEmitter.isDisposed()) {
observableEmitter.onError(e);
}
}
Log.d("SearchActivity", "結束請求,關鍵詞爲:" + query);
observableEmitter.onNext("完成搜索,關鍵詞爲:" + query);
observableEmitter.onComplete();
}
}).subscribeOn(Schedulers.io());
}
@Override
protected void onDestroy() {
super.onDestroy();
mCompositeDisposable.clear();
}
}
複製代碼
運行結果爲: dom
下面,咱們就來詳細的介紹一下這個例子中應用到的三種操做符ide
debounce
的原理圖以下所示: 函數
debounce
原理相似於咱們在收到請求以後,發送一個延時消息給下游,若是在這段延時時間內沒有收到新的請求,那麼下游就會收到該消息;而若是在這段延時時間內收到來新的請求,那麼就會取消以前的消息,並從新發送一個新的延時消息,以此類推。
而若是在這段時間內,上游發送了onComplete
消息,那麼即便沒有到達須要等待的時間,下游也會馬上收到該消息。優化
filter
的原理圖以下所示: spa
filter
的原理很簡單,就是傳入一個
Predicate
函數,其參數爲上游發送的事件,只有該函數返回
true
時,纔會將事件發送給下游,不然就丟棄該事件。
switchMap
的原理是將上游的事件轉換成一個或多個新的
Observable
,可是有一點很重要,就是若是在該節點收到一個新的事件以後,那麼若是以前收到的時間所產生的
Observable
尚未發送事件給下游,那麼下游就不再會收到它發送的事件了。
如上圖所示,該節點前後收到了紅、綠、藍三個事件,並將它們映射成爲紅1、紅2、綠1、綠2、藍1、藍二,可是當藍一發送完事件時,綠二依舊沒有發送事件,而最初綠色事件在藍色事件以前,那麼綠二就不會發送給下游。3d