You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SearchView列表过滤场景下,如何在新查询触发时取消正在运行的协程?

解决快速搜索时协程重复执行的问题

嘿,这个场景太常见了——用户快速敲键盘时,旧的搜索协程还在慢悠悠过滤数据,新的查询又触发了新协程,最后可能导致UI显示的结果和当前输入不匹配,还浪费资源。先说说你尝试的代码问题在哪:你每次调用onQuery都新建了一个job,然后立刻取消它再启动,这根本没触碰到之前正在运行的协程,因为这个新job和旧协程完全没关系呀!

正确的实现方式:跟踪并取消旧协程

我们只需要在ViewModel里维护一个变量,用来保存当前正在运行的搜索协程,每次新查询来的时候,先取消旧的协程,再启动新的。

完整代码示例

import kotlinx.coroutines.Job
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext

// 在你的ViewModel类中
private var searchJob: Job? = null
// 可选:记录上次查询内容,避免重复处理相同查询
private var lastSearchQuery: String = ""

fun onQuery(query: String) {
    // 优化:如果查询内容和上次完全一样,直接返回,不用重复处理
    if (query == lastSearchQuery) return
    lastSearchQuery = query

    // 取消之前正在运行的搜索协程(如果有的话)
    searchJob?.cancel()

    // 启动新的搜索协程,并把引用保存到searchJob
    searchJob = viewModelScope.launch(Dispatchers.Default) {
        // 协程被取消后,直接终止后续操作,避免无用计算
        if (!isActive) return@launch

        // 执行过滤逻辑:这里可以根据需求加忽略大小写等优化
        val personsFound = persons.filter { person ->
            person.nom.contains(query, ignoreCase = true) || 
            person.prenom.contains(query, ignoreCase = true)
        }

        // 再次检查协程状态,避免取消后还去更新UI
        if (!isActive) return@launch

        // 切换到主线程更新UI
        withContext(Dispatchers.Main) {
            _items.value = personsFound
        }
    }
}

关键细节解释

  • searchJob变量:专门用来跟踪当前正在运行的搜索协程,每次新查询触发时,先调用cancel()终止旧协程,确保只有最新的协程能完成过滤并更新UI。
  • isActive检查:协程被取消后,isActive会变为false,提前检查可以避免执行后续的无用代码(比如过滤完成后再切换主线程,这时候协程已经取消了,withContext也会失败,但提前返回更高效)。
  • 忽略大小写优化:添加ignoreCase = true让搜索更符合用户预期,毕竟用户不会在意名字的大小写。
  • 重复查询过滤:记录上次查询内容,避免相同的查询重复触发协程,进一步提升性能。

这样修改后,不管用户敲键盘有多快,始终只有最新的查询协程在运行,旧的会被立刻取消,UI也会始终显示和当前输入匹配的结果。

内容的提问来源于stack exchange,提问作者Ben

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.27 21:18:11