核心前提:
NonCancellable = 临时屏蔽取消信号 ,让代码块原子执行不中断 ,可用于任意代码位置 ,没有强制必须写在 finally 里。
用法1:官方推荐用法 → finally 块中(资源清理/收尾)
适用场景
协程无论成功/失败/取消 ,都必须执行的资源释放操作 (关流、关连接、释放锁);
因为 finally 里的挂起函数 会被取消中断,所以必须用 NonCancellable 包裹。
Android 实战代码
kotlin
import kotlinx.coroutines.*
import androidx.lifecycle.lifecycleScope
// Android 页面中(Activity/Fragment)
lifecycleScope.launch(Dispatchers.IO) {
var networkStream: NetworkStream? = null
try {
// ========== 可取消区域 ==========
// 耗时网络/文件操作,cancel() 会立即中断
networkStream = openNetworkStream()
readStreamData(networkStream)
delay(1000) // 挂起点,可被取消
} finally {
// ========== 必执行区域:协程任何结束方式都会进入 ==========
// 官方规范:finally 中执行挂起收尾,必须用 NonCancellable
withContext(NonCancellable) {
// 纯资源清理,无业务成功逻辑
networkStream?.close() // 挂起函数,不被取消中断
releaseWorkerLock()
log("资源已安全释放")
}
}
}
用法2:项目实战用法 → 业务成功分支中(原子提交)
适用场景
业务已确认成功 (如拿到服务端 resourceId),后续的内存赋值/DB 持久化 必须完整执行,不能被取消中断;
仅成功路径执行,失败/取消不进入。
Android 实战代码
kotlin
import kotlinx.coroutines.*
import androidx.lifecycle.lifecycleScope
// Android 页面中(Activity/Fragment)
lifecycleScope.launch(Dispatchers.IO) {
// ========== 可取消区域 ==========
// 耗时上传操作,cancel() 会立即断开 OkHttp
val resourceId = uploadFileToServer()
// ========== 仅业务成功进入:原子提交区 ==========
if (resourceId != INVALID_ID) {
// 屏蔽取消,保证落盘逻辑完整执行
withContext(NonCancellable) {
// 1. 内存状态赋值
currentItem.remoteId = resourceId
currentItem.status = UploadStatus.SUCCESS
// 2. 本地持久化(DB/SP)
saveUploadResultToDb(currentItem)
// 3. 通知 UI 更新
withContext(Dispatchers.Main) {
updateUploadUi(currentItem)
}
}
}
}
用法3:最佳实践 → 两种用法共存(Android 标准工程代码)
成功分支做原子提交,finally 做资源清理,互不冲突、各司其职。
kotlin
lifecycleScope.launch(Dispatchers.IO) {
var uploadConnection: UploadConnection? = null
try {
// 1. 可取消:建立连接 + 上传文件
uploadConnection = createUploadConnection()
val resourceId = uploadConnection.executeUpload()
// 2. 用法2:业务成功 → 原子提交(你的项目核心逻辑)
if (resourceId != INVALID_ID) {
withContext(NonCancellable) {
currentItem.remoteId = resourceId
saveUploadResultToDb(currentItem)
}
}
} finally {
// 3. 用法1:官方规范 → 无论如何都释放资源
withContext(NonCancellable) {
uploadConnection?.close() // 安全关闭连接
}
}
}
配套:外部取消的标准写法(Android 环境)
kotlin
// 取消上传任务(删除逻辑)
private fun cancelUploadJob(job: Job) {
lifecycleScope.launch {
job.cancel() // 立即中断上传(断开 OkHttp)
job.join() // 等待:原子提交块 + finally 清理 全部执行完毕
// 执行后续删除逻辑
deleteRemoteResource(currentItem.remoteId)
}
}
总结
- 无唯一写法 :
NonCancellable不是必须写在finally中,官方从未规定唯一用法 - 两种标准场景
finally + NonCancellable:官方规范 ,用于协程全生命周期的资源清理成功分支 + NonCancellable:工程实战 ,用于业务结果的原子提交 (你的项目用法)
取消后必须用join()等待不可取消代码执行完毕