使用 Kotlin 流运算符实现即时搜索
流是协程中的一种类型,可以顺序发出多个值,而不是挂起函数,它只返回一个值。例如,流可用于从数据库接收实时更新。流是在协程之上构建的,可以返回多个值。流是可以异步计算的数据流。流的所有输出必须属于同一类型。例如,FlowInt 是一个发出整数值的流。流类似于迭代器,因为它产生一系列值,但它通过使用挂起函数异步地产生和使用值。这意味着,例如,流可以安全地发出网络请求以生成下一个值,而不会阻塞主线程。
以下 Kotlin Flow 功能将用于实现此搜索功能:
- StateFlow:我们已经写了一篇关于它的文章。你可以在这里找到更多关于它的信息。
- 去抖算子
- 过滤器运算符
- 运算符 DistinctUntilChanged
- 最新的 FlatMap 运算符
以前,使用 Kotlin Coroutines 在 Android 中实现这种即时搜索功能很困难,但使用 Kotlin Flow Operators,它变得简单而有趣。
让我们开始吧
首先,我们将编写一个返回 StateFlow 的扩展函数,以便我们可以对其应用必要的运算符。因此,在 SearchView 上,我们将使用 setOnQueryTextListener 来观察文本的变化,改变查询的状态,最后返回 StateFlow,如下所示:
Kotlin
fun GfGSearch.getQueryTextChangeStateFlow(): StateFlow {
val serachQuery = MutableStateFlow("Geeks")
setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onSearchSubmit(query: String?): Boolean {
return true
}
override fun onTermChange(newText: String): Boolean {
serachQuery.value = newText
return true
}
})
return serachQuery
}
Kotlin
// Fake network transaction
private fun someDataFetch(query: String): Flow {
return flow {
delay(1500)
emit(gfgDataQuery)
}
}
Kotlin
gfgSerachView.getQueryTextChangeStateFlow()
.debounce(600)
.filter { serachQuery ->
if (serachQuery.isEmpty()) {
textViewResult.text = "Geeks"
return@filter false
} else {
return@filter true
}
}
.distinctUntilChanged()
.flatMapLatest { serachQuery ->
dataFromNetwork(serachQuery)
.catch {
emitAll(flowOf("Geeks"))
}
}
.flowOn(Dispatchers.Default)
.collect { result ->
sometxtView.text = result
}
然后为了测试这一点,我们对数据进行了一些伪网络交换。
科特林
// Fake network transaction
private fun someDataFetch(query: String): Flow {
return flow {
delay(1500)
emit(gfgDataQuery)
}
}
现在,在 QueryTextChangeStateFlow 上,我们将使用以下运算符:
科特林
gfgSerachView.getQueryTextChangeStateFlow()
.debounce(600)
.filter { serachQuery ->
if (serachQuery.isEmpty()) {
textViewResult.text = "Geeks"
return@filter false
} else {
return@filter true
}
}
.distinctUntilChanged()
.flatMapLatest { serachQuery ->
dataFromNetwork(serachQuery)
.catch {
emitAll(flowOf("Geeks"))
}
}
.flowOn(Dispatchers.Default)
.collect { result ->
sometxtView.text = result
}
是时候了解为什么使用上述运算符以及它们在组合时如何工作了。
识别运算符
- Debounce :在这种情况下,debounce运算符与时间常数一起使用。当用户在短时间内键入“a”、“ab”或“abc”时,debounce运算符会处理这种情况。这样一来,就会有大量的网络调用。然而,用户最终对“abc”搜索结果感兴趣。结果,“a”和“ab”的结果必须被丢弃。理想情况下,不应该有“a”和“ab”的网络调用,因为用户在很短的时间内输入了这些。
- 过滤器:在这种情况下,过滤器运算符用于过滤掉不需要的字符串,它是一个空字符串,以避免进行不必要的网络调用。
- distinctUntilChanged:仅用于避免对所需网络层的重复调用。假设最近正在进行的搜索查询是“abc”,并且用户在再次输入“c”之前删除了“c”。所以它又是“abc”。否则,任何进一步的网络调用都会使搜索返回一个重复的查询,从而增加服务器的负载,从而防止源发出重复的连续项。
- FlatMapLatest:用于在网络中断等情况下不会向用户显示所有预取的陈旧结果。假设最近的搜索查询是“ab”,有一个对“ab”的持续网络调用,并且用户输入了“abc”。然后我们不再对“ab”的结果感兴趣。我们只关心“abc”的结果。结果,flatMapLatest 来救援。它只返回最近的搜索查询的结果,而忽略其余的。
GeekTip: Please keep in mind that if there is an error in the flatMapLatest, we pass the empty result. This can be changed based on our needs.
通过这种方式,我们可以使用 Kotlin Flow Operators 在 Android 应用程序中实现即时搜索功能。