
通过 suspendcancellablecoroutine 将基于回调的异步 api 安全地封装为协程挂起函数,实现“等待结果后再执行下一行”的同步语义,避免阻塞线程且保持代码简洁可读。
在 Kotlin 协程开发中,常会遇到遗留的回调式异步 API(如 Android 的 SettingsClient.checkLocationSettings),它们无法直接返回结果,也不支持 await 语义。你不能也不应使用忙等待、Thread.sleep 或 CountDownLatch 等方式“强行阻塞”主线程——这不仅违背协程非阻塞的设计哲学,还会导致 ANR 或 UI 冻结。
正确做法是:将回调桥接为挂起函数。核心工具是 suspendCancellableCoroutine,它允许你在挂起当前协程的同时注册回调,并在回调触发时恢复执行。
以下是一个完整、健壮的封装示例:
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlin.coroutines.resume
// 封装 areAllTheSettingsGranted 回调为挂起函数
suspend fun Settings.areAllTheSettingsGrantedSuspend(): Boolean {
return suspendCancellableCoroutine { cont ->
this.areAllTheSettingsGranted { isGranted ->
cont.resume(isGranted as Boolean)
}
// 可选:处理取消(例如清理资源)
cont.invokeOnCancellation {
// 如需取消逻辑(如取消 pending 请求),在此添加
}
}
}
// 同理封装内部的 checkIfLocationServicesAreEnabled
suspend fun LocationSettingsManager.checkIfLocationServicesAreEnabledSuspend(): Boolean {
return suspendCancellableCoroutine { cont ->
checkIfLocationServicesAreEnabled { result ->
cont.resume(result)
}
}
}使用时,只需在协程作用域中调用即可获得“同步感”:
lifecycleScope.launch {
try {
val allGranted = settings.areAllTheSettingsGrantedSuspend()
if (allGranted) {
// ✅ 此处 result 已确定,安全使用
startLocationUpdates()
} else {
requestPermissions()
}
} catch (e: CancellationException) {
// 协程被取消(如 Activity 销毁),自动处理
throw e
}
}⚠️ 重要注意事项:
- ✅ 必须在协程作用域(如 lifecycleScope、viewModelScope)中调用,否则会编译失败;
- ✅ suspendCancellableCoroutine 自动支持协程取消,invokeOnCancellation 可用于释放资源;
- ❌ 不要滥用 runBlocking 包裹此类调用——它会阻塞线程,仅适用于测试或极少数特殊场景;
- ? 若原始回调可能被多次调用(如流式事件),需确保只调用 cont.resume() 一次,否则抛出 IllegalStateException;必要时可用 cont.tryResume() 做防护。
总结:Kotlin 协程不提供“强制同步”的魔法开关,但通过 suspendCancellableCoroutine,你能以声明式、无锁、可取消的方式,优雅地将回调驱动的异步逻辑转化为直观的顺序代码流——这才是现代 Android/Kotlin 异步编程的推荐实践。









