📜  使用 Kotlin 流运算符实现即时搜索

📅  最后修改于: 2022-05-13 01:54:40.178000             🧑  作者: Mango

使用 Kotlin 流运算符实现即时搜索

流是协程中的一种类型,可以顺序发出多个值,而不是挂起函数,它只返回一个值。例如,流可用于从数据库接收实时更新。流是在协程之上构建的,可以返回多个值。流是可以异步计算的数据流。流的所有输出必须属于同一类型。例如,FlowInt 是一个发出整数值的流。流类似于迭代器,因为它产生一系列值,但它通过使用挂起函数异步地产生和使用值。这意味着,例如,流可以安全地发出网络请求以生成下一个值,而不会阻塞主线程。

以下 Kotlin Flow 功能将用于实现此搜索功能:

  1. StateFlow:我们已经写了一篇关于它的文章。你可以在这里找到更多关于它的信息。
  2. 去抖算子
  3. 过滤器运算符
  4. 运算符 DistinctUntilChanged
  5. 最新的 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
    }

是时候了解为什么使用上述运算符以及它们在组合时如何工作了。

识别运算符

  1. Debounce :在这种情况下,debounce运算符与时间常数一起使用。当用户在短时间内键入“a”、“ab”或“abc”时,debounce运算符会处理这种情况。这样一来,就会有大量的网络调用。然而,用户最终对“abc”搜索结果感兴趣。结果,“a”和“ab”的结果必须被丢弃。理想情况下,不应该有“a”和“ab”的网络调用,因为用户在很短的时间内输入了这些。
  2. 过滤器:在这种情况下,过滤器运算符用于过滤掉不需要的字符串,它是一个空字符串,以避免进行不必要的网络调用。
  3. distinctUntilChanged:仅用于避免对所需网络层的重复调用。假设最近正在进行的搜索查询是“abc”,并且用户在再次输入“c”之前删除了“c”。所以它又是“abc”。否则,任何进一步的网络调用都会使搜索返回一个重复的查询,从而增加服务器的负载,从而防止源发出重复的连续项。
  4. FlatMapLatest:用于在网络中断等情况下不会向用户显示所有预取的陈旧结果。假设最近的搜索查询是“ab”,有一个对“ab”的持续网络调用,并且用户输入了“abc”。然后我们不再对“ab”的结果感兴趣。我们只关心“abc”的结果。结果,flatMapLatest 来救援。它只返回最近的搜索查询的结果,而忽略其余的。

通过这种方式,我们可以使用 Kotlin Flow Operators 在 Android 应用程序中实现即时搜索功能。