【问题标题】:How to implement search feature with SearchView, Retrofit and RxJava (RxBindings)?如何使用 SearchView、Retrofit 和 RxJava (RxBindings) 实现搜索功能?
【发布时间】:2017-01-26 07:12:01
【问题描述】:

当用户输入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()) 时,搜索 API 调用会在创建片段时触发,并且在 SearchView 中没有输入任何查询,我得到异常

retrofit2.adapter.rxjava.HttpException: HTTP 422 

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

【问题讨论】:

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


    【解决方案1】:

    请记住,您实际上可以在 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(...)
                    }
                });
    

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

    【讨论】:

    • 否则配置Retrofit时可以指定Io线程 -- .addCallAdapterFactory(RxJavaCallAdapterFactory.create(Schedulers.io))
    • 是的,然后.observeOn(Schedulers.io()) 就没有必要了
    【解决方案2】:

    我在生产应用中使用这种方法。

     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 来救援了。它只是 提供最后一个搜索查询的结果(最近的)和 忽略其余部分。

    【讨论】:

    • 嗨,你能分享你的 dataFromNetwork() 代码吗?如何使用改造和 Livedata 从我的 ViewModel 中获取它?
    猜你喜欢
    • 2018-06-08
    • 2023-03-15
    • 1970-01-01
    • 1970-01-01
    • 2021-01-09
    • 2015-09-24
    • 2021-01-08
    • 2022-07-07
    相关资源
    最近更新 更多