繁体   English   中英

RxJava:如何重置长时间运行的热可观察链?

[英]RxJava: How can I reset a long running hot observable chain?

对于我的应用程序的搜索功能,我有一个热门的可观察链,该链可进行以下工作。

  1. 将用户输入的字符串mainThread EditTextTextChangedEvent )中(在mainThread
  2. 防抖动300ms(在computation线程上)
  3. 显示加载微调器( mainThread
  4. 使用该字符串查询SQL db(此查询可能需要100ms到2000ms的任何时间)(在Schedulers.io()
  5. 向用户显示结果( mainThread

由于步骤3的长度是可变的,因此会发生竞态条件,其中显示的是较新的搜索结果而不是较新的结果(有时)。 假设用户想输入chicken ,但是由于输入速度异常,该单词的第一部分在整个术语之前发出:

  • 首先发出对chick的搜索,然后是chicken
  • 比如说chick需要1500ms 300ms执行,而chicken需要300ms执行。
  • 这会导致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();
                    }
                });

使用switchMap而不是flatMap 每当您开始新查询时,这将导致它丢弃*前一个查询。

*这是如何工作的:

每当外部源observable产生一个新值时, switchMap调用您的选择器以返回一个新的内部observable(在这种情况下为presenter.getAdapterViewModelRx(s) )。 然后, switchMap 取消订阅它正在侦听的先前内部观察,并订阅新的内部观察。

取消订阅先前的内部可观察对象有两个效果:

  1. 可观察对象产生的任何通知(值,完成,错误等)将被静默忽略并丢弃。

  2. 将向可观察对象通知其观察者已取消订阅,并且可以选择采取步骤取消它所代表的任何异步过程。

您放弃的查询是否实际上被取消完全取决于presenter.getAdapterViewModelRx()的实现。 理想情况下,将其取消以避免不必要地浪费服务器资源。 但是,即使它们继续运行,上面的#1也可以防止您的预输入代码看到过时的结果。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM