如何使用SearchView,Retrofit和RxJava(RxBindings)实现搜索功能?

时间:2016-09-18 14:43:18

标签: java android retrofit rx-java rx-binding

当用户输入SearchView窗口小部件时,应用应该创建一个 API调用(后台线程中的 )从服务器获取搜索结果,并在RecyclerView中显示它们(在UI线程中)。

我在片段中使用以下代码:

@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
    inflater.inflate(R.menu.my_fragment, menu);
    SearchView searchView = (SearchView) menu.findItem(R.id.action_search).getActionView();

    SearchManager searchManager = (SearchManager) getActivity().getSystemService(Context.SEARCH_SERVICE);
    searchView.setSearchableInfo(searchManager.getSearchableInfo(getActivity().getComponentName()));

    RxSearchView.queryTextChanges(searchView)
                .debounce(400, TimeUnit.MILLISECONDS)
                .map(CharSequence::toString)
                .switchMap(query -> retrofitService.search(query))
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Subscriber<List<Item>>() {
                    @Override
                    public void onCompleted() {

                    }

                    @Override
                    public void onError(Throwable e) {
                        Log.e(LOG_TAG, "Error", e);
                    }

                    @Override
                    public void onNext(List<Item> items) {
                        // adapter.addItems(...)
                    }
                });
}

但我得到一个例外:

java.lang.IllegalStateException: Must be called from the main thread. Was: Thread[RxIoScheduler-2,5,main]
at com.jakewharton.rxbinding.internal.Preconditions.checkUiThread(Preconditions.java:35)
at com.jakewharton.rxbinding.support.v7.widget.SearchViewQueryTextChangesOnSubscribe.call(SearchViewQueryTextChangesOnSubscribe.java:18)
at com.jakewharton.rxbinding.support.v7.widget.SearchViewQueryTextChangesOnSubscribe.call(SearchViewQueryTextChangesOnSubscribe.java:10)
...

当我删除.subscribeOn(Schedulers.io())时,在创建片段并且SearchView中没有输入查询并且我获得了exeption

时会触发搜索API调用
retrofit2.adapter.rxjava.HttpException: HTTP 422 

然后,当我输入我的搜索查询时,retrofitService.search(query)不再被调用。

2 个答案:

答案 0 :(得分:17)

请记住,您实际上可以在rx链中使用多个observeOn和多个subscribeOn运算符。

试试这个:

RxSearchView.queryTextChanges(searchView)
            .debounce(400, TimeUnit.MILLISECONDS)
            .map(CharSequence::toString)
            .subscribeOn(AndroidSchedulers.mainThread())
            .observeOn(Schedulers.io())
            .switchMap(query -> retrofitService.search(query))
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(new Subscriber<List<Item>>() {
                @Override
                public void onCompleted() {

                }

                @Override
                public void onError(Throwable e) {
                    Log.e(LOG_TAG, "Error", e);
                }

                @Override
                public void onNext(List<Item> items) {
                    // adapter.addItems(...)
                }
            });

这基本上会导致这个线程使用:

thread usage

答案 1 :(得分:1)

我在生产应用程序上使用了这种方法。

 RxSearchView.queryTextChanges(searchView)
                    .debounce(300, TimeUnit.MILLISECONDS)
                    .filter(new Predicate<String>() {
                        @Override
                        public boolean test(String text) throws Exception {
                            if (text.isEmpty()) {
                                return false;
                            } else {
                                return true;
                            }
                        }
                    })
                    .distinctUntilChanged()
                    .switchMap(new Function<String, ObservableSource<String>>() {
                        @Override
                        public ObservableSource<String> apply(String query) throws Exception {
                            return dataFromNetwork(query);
                        }
                    })
                    .subscribeOn(Schedulers.io())
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribe(new Consumer<String>() {
                        @Override
                        public void accept(String result) throws Exception {
                            textViewResult.setText(result);
                        }
                    });
  • DistinctUntilChanged:distinctUntilChanged运算符用于 避免重复的网络呼叫。假设最近正在进行的搜索 查询为“ abc”,用户删除了“ c”,然后再次键入“ c”。所以 再次是“ abc”。因此,如果网络通话已经与 搜索查询“ abc”,它将不会再次使用 搜索查询“ abc”。因此,distinctUntilChanged抑制重复项 源Observable发出的连续项。
  • SwitchMap:此处,使用switchMap运算符来避免网络 不需要再显示给用户的通话结果。 假设最后一个搜索查询为“ ab”,并且正在进行 网络呼叫“ ab”,用户键入“ abc”。那你就没有了 对“ ab”的结果感兴趣。您只对 “ abc”的结果。因此,switchMap可以解决。它只是 提供最后搜索查询(最新)的结果,以及 忽略其余部分。