如何结合LiveData与RxSearchView?RxBinding实现优化及Flowable选型疑问
嘿,这个场景我之前也折腾过,咱们一步步捋清楚~
结合LiveData与RxSearchView的实现思路
一、当前常见的合理实现方式
通常我们会借助RxSearchView.queryTextChanges(searchView)获取输入流,再通过switchMap切换到新的搜索请求Observable,最后转成LiveData给UI订阅。这里给个典型的实现代码:
// 假设你有一个Repository层的搜索方法 fun search(query: String): Observable<List<SearchResult>> { return apiService.search(query) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) } // ViewModel中的实现 val searchQueryLiveData = MutableLiveData<String>() // 转换Rx流到LiveData val searchResultsLiveData: LiveData<List<SearchResult>> = Transformations.switchMap(searchQueryLiveData) { query -> if (query.isBlank()) { return@switchMap MutableLiveData(emptyList()) } // 用RxJava的Observable转LiveData ObservableLiveData( repository.search(query) .doOnError { /* 处理错误,比如发送错误事件 */ } .onErrorReturn { emptyList() } ) } // 绑定SearchView的输入流到searchQueryLiveData RxSearchView.queryTextChanges(searchView) .debounce(300, TimeUnit.MILLISECONDS) // 防抖,避免频繁请求 .map { it.toString() } .subscribe { searchQueryLiveData.value = it } .addTo(compositeDisposable)
这个实现是完全合理的:
- 用
debounce过滤掉短时间内的频繁输入,减少无效请求 switchMap会自动取消前一个未完成的请求,只保留最新的搜索请求,避免旧结果覆盖新结果- 通过
ObservableLiveData把Rx流转换成LiveData,适配Jetpack的生命周期感知
二、其他替代实现方式
如果你不想依赖Transformations.switchMap,也可以直接在Rx流中处理切换,然后转成LiveData:
val searchResultsLiveData = MutableLiveData<List<SearchResult>>() RxSearchView.queryTextChanges(searchView) .debounce(300, TimeUnit.MILLISECONDS) .map { it.toString() } .filter { it.isNotBlank() } .switchMap { query -> repository.search(query) .onErrorReturn { emptyList() } } .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe { searchResultsLiveData.value = it } .addTo(compositeDisposable)
这种方式更偏向纯Rx的写法,代码更连贯,但要注意手动管理Disposable,别搞出内存泄漏哦。
另外,如果你项目已经大量用Kotlin协程,也可以结合callbackFlow把RxSearchView的流转换成Flow,再转成LiveData:
val searchResultsLiveData = searchView.queryTextChangesFlow() .debounce(300) .filter { it.isNotBlank() } .flatMapLatest { query -> flow { emit(repository.searchCoroutine(query)) }.catch { emit(emptyList()) } } .flowOn(Dispatchers.IO) .asLiveData() // 扩展函数把RxSearchView转成Flow fun SearchView.queryTextChangesFlow(): Flow<String> = callbackFlow { val disposable = RxSearchView.queryTextChanges(this@queryTextChangesFlow) .map { it.toString() } .subscribe { trySend(it) } awaitClose { disposable.dispose() } }
这种方式更贴合Jetpack的协程生态,代码风格会更统一。
三、是否应该改用Flowable?
这个要看你的搜索场景:
- 如果你的搜索请求可能会产生大量数据(比如分页加载很多条,或者流是无限的),或者需要背压处理(比如输入太快导致请求堆积,虽然我们已经用了debounce和switchMap,但极端情况还是可能有),那用
Flowable会更合适,因为Flowable天生支持背压策略。 - 如果只是普通的单次搜索请求,返回有限的结果,那
Observable完全够用,没必要换成Flowable,反而会增加一点复杂度。
比如把上面的Observable换成Flowable的话,代码改动不大:
fun search(query: String): Flowable<List<SearchResult>> { return apiService.search(query) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) } // 对应的switchMap部分也换成Flowable的处理即可
总结下来,你的初始实现思路是没问题的,具体选哪种方式取决于项目的技术栈和需求场景~
内容的提问来源于stack exchange,提问作者Deep
相关产品推荐
相关产品推荐

