2

私のアプリの検索機能には、次のことを行うホット オブザーバブル チェーンがあります。

  1. ユーザー入力文字列をEditText(a TextChangedEvent) に受け入れます (on mainThread)
  2. デバウンス 300ms (computationスレッド上)
  3. ローディングスピナーを表示 ( mainThread)
  4. その文字列を使用して SQL データベースにクエリを実行します (このクエリには 100 ミリ秒から 2000 ミリ秒かかる場合があります) (on Schedulers.io())
  5. 結果をユーザーに表示する ( mainThread)

ステップ 3 の長さは非常に可変であるため、最近の検索結果よりも新しい検索結果が表示される競合状態が発生します (場合によっては)。ユーザーが を入力したいとしますがchicken、入力速度が異常なため、単語の最初の部分が単語全体の前に出力されます。

  • の検索chickが最初に送信され、続いて が送信されchickenます。
  • 実行にchickかかると言いますが、実行にかかります。1500mschicken300ms
  • これにより、chick検索語に対する検索結果が正しく表示されなくなりますchicken。これは、chicken検索が最初に完了し (わずか 300 ミリ秒しかかからなかった)、続いて検索が完了したためchickです (1500 ミリ秒)。

このシナリオをどのように処理できますか?

  • ユーザーが を介して新しい検索をトリガーするTextChangedEventと、古い検索がまだ実行されていても、私は気にしません。古い検索をキャンセルする方法はありますか?

完全な観察可能なコード:

subscription = WidgetObservable.text(searchText)
                .debounce(300, TimeUnit.MILLISECONDS)
                .observeOn(AndroidSchedulers.mainThread())
                        //do this on main thread because it's a UI element (cannot access a View from a background thread)

                        //get a String representing the new text entered in the EditText
                .map(new Func1<OnTextChangeEvent, String>() {
                    @Override
                    public String call(OnTextChangeEvent onTextChangeEvent) {
                        return onTextChangeEvent.text().toString().trim();
                    }
                })
                .subscribeOn(AndroidSchedulers.mainThread())
                .doOnNext(new Action1<String>() {
                    @Override
                    public void call(String s) {
                        presenter.handleInput(s);
                    }
                })
                .subscribeOn(AndroidSchedulers.mainThread())
                .observeOn(Schedulers.io())
                .filter(new Func1<String, Boolean>() {
                    @Override
                    public Boolean call(String s) {
                        return s != null && s.length() >= 1 && !s.equals("");
                    }
                }).doOnNext(new Action1<String>() {
                    @Override
                    public void call(String s) {
                        Timber.d("searching for string: '%s'", s);
                    }
                })
                        //run SQL query and get a cursor for all the possible search results with the entered search term
                .flatMap(new Func1<String, Observable<SearchBookmarkableAdapterViewModel>>() {
                    @Override
                    public Observable<SearchBookmarkableAdapterViewModel> call(String s) {
                        return presenter.getAdapterViewModelRx(s);
                    }
                })
                .subscribeOn(Schedulers.io())
                        //have the subscriber (the adapter) run on the main thread
                .observeOn(AndroidSchedulers.mainThread())
                        //subscribe the adapter, which receives a stream containing a list of my search result objects and populates the view with them
                .subscribe(new Subscriber<SearchBookmarkableAdapterViewModel>() {
                    @Override
                    public void onCompleted() {
                        Timber.v("Completed loading results");
                    }

                    @Override
                    public void onError(Throwable e) {
                        Timber.e(e, "Error loading results");
                        presenter.onNoResults();
                        //resubscribe so the observable keeps working.
                        subscribeSearchText();
                    }

                    @Override
                    public void onNext(SearchBookmarkableAdapterViewModel searchBookmarkableAdapterViewModel) {
                        Timber.v("Loading data with size: %d into adapter", searchBookmarkableAdapterViewModel.getSize());
                        adapter.loadDataIntoAdapter(searchBookmarkableAdapterViewModel);
                        final int resultCount = searchBookmarkableAdapterViewModel.getSize();
                        if (resultCount == 0)
                            presenter.onNoResults();
                        else
                            presenter.onResults();
                    }
                });
4

1 に答える 1