如何使用 SearchView、Retrofit 和 RxJava (RxBindings) 实现搜索功能?
How to implement search feature with SearchView, Retrofit and RxJava (RxBindings)?
当用户在 SearchView
小部件中键入内容时,应用程序应生成一个
API调用(在后台线程)从服务器获取搜索结果,并显示它们(在UI线程)在 RecyclerView 中。
我在片段中使用了以下代码:
@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)
不再被调用。
请记住,您实际上可以在 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(...)
}
});
这基本上会导致线程使用:
我在生产应用程序中使用了这种方法。
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 来拯救。它只是
提供最后一次搜索查询(最近)的结果和
忽略其余部分。
当用户在 SearchView
小部件中键入内容时,应用程序应生成一个
API调用(在后台线程)从服务器获取搜索结果,并显示它们(在UI线程)在 RecyclerView 中。
我在片段中使用了以下代码:
@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)
不再被调用。
请记住,您实际上可以在 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(...)
}
});
这基本上会导致线程使用:
我在生产应用程序中使用了这种方法。
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 来拯救。它只是 提供最后一次搜索查询(最近)的结果和 忽略其余部分。