RxJava2 實戰知識梳理(3) 優化搜索聯想功能

1、示例

1.1 應用場景

幾乎每一個應用程序都提供了搜索功能,某些應用還提供了搜索聯想。對於一個搜索聯想功能,最基本的實現流程爲:客戶端經過EditTextaddTextChangedListener方法監聽輸入框的變化,當輸入框發生變化以後就會回調afterTextChanged方法,客戶端利用當前輸入框內的文字向服務器發起請求,服務器返回與該搜索文字關聯的結果給客戶端進行展現。java

在該場景下,有幾個能夠優化的方面:服務器

  • 在用戶連續輸入的狀況下,可能會發起某些沒必要要的請求。例如用戶輸入了abc,那麼按照上面的實現,客戶端就會發起aababc三個請求。
  • 當搜索詞爲空時,不該該發起請求。
  • 若是用戶依次輸入了ababc,那麼首先會發起關鍵詞爲ab請求,以後再發起abc的請求,可是abc的請求若是先於ab的請求返回,那麼就會形成用戶指望搜索的結果爲abc,最終展示的結果倒是和ab關聯的。

1.2 示例代碼

這裏,咱們針對上面提到的三個問題,使用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

2、示例解析

下面,咱們就來詳細的介紹一下這個例子中應用到的三種操做符ide

2.1 debounce

debounce的原理圖以下所示: 函數

debounce原理相似於咱們在收到請求以後,發送一個延時消息給下游,若是在這段延時時間內沒有收到新的請求,那麼下游就會收到該消息;而若是在這段延時時間內收到來新的請求,那麼就會取消以前的消息,並從新發送一個新的延時消息,以此類推。

而若是在這段時間內,上游發送了onComplete消息,那麼即便沒有到達須要等待的時間,下游也會馬上收到該消息。優化

2.2 filter

filter的原理圖以下所示: spa

filter的原理很簡單,就是傳入一個 Predicate函數,其參數爲上游發送的事件,只有該函數返回 true時,纔會將事件發送給下游,不然就丟棄該事件。

2.3 switchMap

switchMap的原理是將上游的事件轉換成一個或多個新的 Observable,可是有一點很重要,就是若是在該節點收到一個新的事件以後,那麼若是以前收到的時間所產生的 Observable尚未發送事件給下游,那麼下游就不再會收到它發送的事件了。

如上圖所示,該節點前後收到了紅、綠、藍三個事件,並將它們映射成爲紅1、紅2、綠1、綠2、藍1、藍二,可是當藍一發送完事件時,綠二依舊沒有發送事件,而最初綠色事件在藍色事件以前,那麼綠二就不會發送給下游。3d


更多文章,歡迎訪問個人 Android 知識梳理系列:

相關文章
相關標籤/搜索