如何停止或取消Kotlin Coroutine,立即终止当前运行的代码块?
协程无法立刻终止阻塞任务的解决方案
根本原因
协程的取消是协作式的,只有执行到挂起函数、或者代码主动检查isActive状态时才会生效。你遇到的阻塞IO(网络下载、文件读写)、CPU密集循环这类逻辑本身不会主动响应协程取消标记,所以哪怕调用job.cancel()、scope.cancel(),也会等当前阻塞操作执行完才会终止协程,这个特性和线程、线程池的中断逻辑一致,系统只会给线程打中断标记,需要运行的代码主动检查标记才会停止。
无第三方依赖的实现方案
1. CPU密集循环场景
每次循环主动检查协程状态即可,两种实现方式:
// 方式1:手动判断isActive跳出循环 for (i in 0..1000000) { if (!isActive) break // 执行运算逻辑 } // 方式2:调用ensureActive(),取消时直接抛出CancellationException终止协程 for (i in 0..1000000) { ensureActive() // 执行运算逻辑 }
2. 网络下载场景
核心是协程取消时主动关闭网络连接,强制终止阻塞IO操作,原生HttpURLConnection实现示例如下:
fun downloadImage(imageUrl: String, scope: CoroutineScope): Job { var urlConnection: HttpURLConnection? = null return scope.launch(Dispatchers.IO) { try { val url = URL(imageUrl) urlConnection = url.openConnection() as HttpURLConnection urlConnection?.connect() val inputStream = urlConnection?.inputStream ?: return@launch // 流读取、存本地/转Bitmap逻辑 inputStream.use { input -> val outputStream = // 你的输出流(文件/内存缓存) val buffer = ByteArray(8192) var bytesRead: Int while (input.read(buffer).also { bytesRead = it } != -1) { ensureActive() // 每次读取前检查协程状态 outputStream.write(buffer, 0, bytesRead) } } } catch (e: Exception) { // 协程取消、连接关闭会抛出异常,捕获后做异常处理即可 } finally { urlConnection?.disconnect() } }.apply { invokeOnCompletion { // 协程结束(正常/取消)时主动关闭连接释放资源 urlConnection?.disconnect() } } }
调用逻辑:
// 旧任务取消 oldDownloadJob?.cancel() // 启动新下载任务 oldDownloadJob = downloadImage("新图片地址", lifecycleScope)
列表滚动场景优化建议
- 每个列表项绑定单独的下载Job,列表项滑出屏幕、或者ViewHolder复用时,先取消上一个绑定的下载Job,再启动新的下载任务。
- 新增内存缓存、磁盘缓存逻辑,已经下载过的图片直接读缓存,不需要重复发起请求。
内容的提问来源于stack exchange,提问作者Abdur Rahman
相关产品推荐
相关产品推荐

