一. 问题引出 现在几乎所有的 App 都有搜索功能 , 一般情况我们监听 EditText 控件,当值发生改变去请求搜索接口. 如:
etSearch.doOnTextChanged { text, start, before, count ->
search(text.toString())
}
这样就会出现两个问题:
- 可能导致很多没有意义的请求,耗费用户流量(因为控件的值每更改一次立即就会去请求网络,而且只是最后输入的关键字是有用的)
- 可能导致最终搜索的结果不是用户想要的. 例如,用户一开始输入关键字 AB 这个时候出现两个请求, 一个请求是 A 关键字, 一个请求是 AB 关键字. 表面上是 A 请求先发出去, AB 请求后发出去. 如果后发出去的 AB 请求先返回, A 请求后返回,那么 A 请求后的结果将会覆盖 AB 请求的结果. 从而导致搜索结果不正确.
二. 解决问题 使用Kotlin Flow 的 debounce,flatMapLatest等操作符可以解决这个问题。
val stateFlow = MutableStateFlow("")
private fun searchFilter(){
stateFlow
.debounce(500)
.filter {
it.isNotEmpty()
}
.flatMapLatest {
getFlowList(it)
}
.catch { print(it.message) }
.flowOn(Dispatchers.Default)
.onEach {
print(it.toString())
}.flowOn(Dispatchers.Main)
.launchIn(lifecycleScope)
}
上面代码的主要逻辑:
- stateFlow:一个状态容器式可观察数据流,可以向其收集器发出当前状态更新和新状态更新
- 使用 debounce 操作符:只有允许间隔超过500ms间隔才能触发,避免过多的请求
- 使用 filter 操作符: 只有关键词不为空才进行请求,避免空的输入值也请求
- 使用 flatMap 操作符:如果前面的请求没有完成,直接取消,然后开始先的请求
- 使用 catch 操作符:捕获异常,释放空的List