簡體   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