Kotlin中停止并重启协程的最佳实现方式(安卓绘图场景)
Android滑块滑动场景下Kotlin协程绘制任务的取消重启方案
这种频繁触发、需要废弃旧任务执行新任务的场景,核心实现思路是主动持有绘制协程的Job引用,每次触发新任务前先取消旧Job,再启动新协程赋值给Job变量,可以配合防抖逻辑优化性能,具体实现如下:
基础实现方案
直接在滑动回调里管理绘制协程的生命周期,适合需要在滑动过程中实时更新图表的场景:
- 声明全局可变Job变量,用于持有当前正在运行的绘制协程实例
- 滑块滑动回调触发时,先调用旧Job的
cancel()方法取消未完成的绘制任务 - 启动新的协程执行绘制逻辑,将新协程的Job赋值给全局变量
- 绘制逻辑要加入协程协作取消的检查,避免任务无法正常退出
代码示例:
// 全局持有绘制任务的Job private var chartDrawJob: Job? = null // 滑块滑动监听(View体系示例) seekBar.setOnSeekBarChangeListener(object : OnSeekBarChangeListener { override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) { // 取消上一次未完成的绘制任务 chartDrawJob?.cancel() // 启动新的绘制协程,绑定View生命周期,页面销毁时自动取消 chartDrawJob = lifecycleScope.launch { drawChart(progress) } } override fun onStartTrackingTouch(seekBar: SeekBar?) {} override fun onStopTrackingTouch(seekBar: SeekBar?) { // 滑块停止时执行最终的完整绘制 chartDrawJob?.cancel() chartDrawJob = lifecycleScope.launch { drawChart(seekBar?.progress ?: 0, isFinal = true) } } }) // 支持协程取消的绘制方法 private suspend fun drawChart(progress: Int, isFinal: Boolean = false) { // 耗时计算阶段增加isActive检查,取消时直接退出 val chartData = withContext(Dispatchers.Default) { if (!isActive) return@withContext emptyList() // 执行耗时的图表数据计算逻辑 computeChartData(progress, isFinal) } if (!isActive) return // 主线程绘制阶段也可增加取消检查 withContext(Dispatchers.Main) { if (!isActive) return@withContext chartView.updateData(chartData) chartView.invalidate() } }
优化方案:增加防抖处理
如果不需要滑动过程中实时更新图表,只需要滑块停止后更新,可以用防抖逻辑减少不必要的任务创建和销毁开销:
// 定义接收滑动进度的SharedFlow private val slideProgressFlow = MutableSharedFlow<Int>(extraBufferCapacity = 1) init { // 初始化时监听滑动事件流 lifecycleScope.launch { slideProgressFlow // 设置200ms防抖,间隔小于200ms的滑动事件会被过滤,仅处理最后一次 .debounce(200) .collect { progress -> drawChart(progress, isFinal = true) } } } // 滑动回调仅发送事件,不需要手动管理Job override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) { slideProgressFlow.tryEmit(progress) }
注意事项
- 绘制逻辑的各个耗时节点必须加
isActive状态检查,确保协程取消时能及时终止,避免无效计算占用资源 - 协程必须绑定到对应的生命周期域:View层用
lifecycleScope,业务逻辑层用viewModelScope,页面销毁时会自动取消所有未完成的协程,避免内存泄漏 - 如果绘制过程中有打开文件、申请硬件资源等不可取消的操作,需要在
finally代码块中完成资源释放
内容的提问来源于stack exchange,提问作者votopec
相关产品推荐
相关产品推荐

