Kotlin中同一挂起函数重复调用时取消正在运行的前序任务
Kotlin实现点击防抖、自动取消前序执行任务的方案
Kotlin协程原生支持该能力,不需要额外依赖第三方库,核心是利用协程Job的可取消特性,就能实现「新调用触发时自动终止还在delay等待阶段的前序任务,仅保留最后一次连续点击的日志输出」的需求。
核心实现逻辑
- 维护一个协程Job变量,用来持有当前正在执行的计数器递增任务引用
- 每次点击触发新任务前,先调用前序Job的
cancel()方法,直接中断未执行完的前序任务 - 协程的
delay()是标准的可取消挂起函数,任务被取消时会立刻终止等待流程,不会继续执行后续的日志打印逻辑
适配现有代码的实现示例
示例代码用到了mutableStateOf,适配Compose/Android开发场景,建议绑定生命周期感知的协程作用域避免内存泄漏:
// 持有正在运行的计数器任务引用 private var addCounterJob: Job? = null // 请替换为当前场景对应的生命周期绑定作用域: // Compose中用rememberCoroutineScope(),ViewModel中用viewModelScope,页面中用lifecycleScope private val scope = rememberCoroutineScope() val counter = mutableStateOf(0) // 点击事件直接绑定这个方法 fun onCounterClick() { // 新点击到来,先取消上一个还在等待的任务 addCounterJob?.cancel() // 启动新的协程任务 addCounterJob = scope.launch { addCounter() } } suspend fun addCounter(){ delay(2000) // === 逻辑位置调整说明 === // 1. 如果需要只有最后一次有效点击才累加计数:把counter.value++放在delay后面,和日志同位置 // 2. 如果需要每次点击都立刻累加计数,只是不打印中间过程的日志:把counter.value++挪到onCounterClick里、cancel()之后的位置 counter.value++ Log.e("click cat","click $counter") }
可选场景调整
如果你需要每次点击都立刻更新计数,只是过滤掉2秒内连续点击的重复日志,直接把计数累加逻辑挪到点击入口即可:
fun onCounterClick() { addCounterJob?.cancel() counter.value++ // 每次点击立刻加1,不受协程取消影响 addCounterJob = scope.launch { delay(2000) Log.e("click cat","click $counter") } }
注意:不要用GlobalScope启动这类和UI绑定的协程,否则会脱离生命周期管控造成内存泄漏。
内容的提问来源于stack exchange,提问作者cattarantadoughan
相关产品推荐
相关产品推荐

