Kotlin 协程提供了多种将传统异步代码转换为挂起函数的技术。本文详细介绍四种核心方案及其适用场景。
一、suspendCancellableCoroutine
1.1 概念
suspendCancellableCoroutine 是 Kotlin 协程库中的底层 API,用于将基于回调的单次异步操作转换为挂起函数。它是 suspendCoroutine 的可取消版本,支持协程取消时的资源清理。
1.2 基本语法
suspend fun <T> asyncOperation(): T = suspendCancellableCoroutine { continuation ->
// 执行异步操作
someAsyncCallback(
onSuccess = { result ->
continuation.resume(result)
},
onFailure = { error ->
continuation.resumeWithException(error)
}
)
// 处理取消(可选)
continuation.invokeOnCancellation {
// 协程被取消时的清理工作
cancelAsyncOperation()
}
}
1.3 实际应用示例
示例一:Retrofit Call 转挂起函数
suspend fun <T> Call<T>.await(): T = suspendCancellableCoroutine { cont ->
enqueue(object : Callback<T> {
override fun onResponse(call: Call<T>, response: Response<T>) {
if (response.isSuccessful) {
cont.resume(response.body()!!)
} else {
cont.resumeWithException(HttpException(response))
}
}
override fun onFailure(call: Call<T>, t: Throwable) {
cont.resumeWithException(t)
}
})
// 取消时取消网络请求
cont.invokeOnCancellation { cancel() }
}
示例二:SharedPreferences 异步提交
suspend fun SharedPreferences.commitAsync(): Boolean = suspendCancellableCoroutine { cont ->
val editor = edit()
// 假设有一些 put 操作
editor.putBoolean("key", true)
editor.commit { success ->
if (success) {
cont.resume(true)
} else {
cont.resumeWithException(IOException("Commit failed"))
}
}
}
示例三:Android BroadcastReceiver
suspend fun Context.awaitBroadcast(action: String): Intent = suspendCancellableCoroutine { cont ->
val receiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
cont.resume(intent)
}
}
val filter = IntentFilter(action)
registerReceiver(receiver, filter)
cont.invokeOnCancellation {
unregisterReceiver(receiver)
}
}
1.4 suspendCoroutine vs suspendCancellableCoroutine
// suspendCoroutine - 不支持取消
suspend fun <T> simpleAsync(): T = suspendCoroutine { cont ->
callback { result -> cont.resume(result) }
// 协程取消时无法清理资源
}
// suspendCancellableCoroutine - 支持取消(推荐)
suspend fun <T> cancellableAsync(): T = suspendCancellableCoroutine { cont ->
callback { result -> cont.resume(result) }
cont.invokeOnCancellation { cleanup() } // 可清理资源
}
特性suspendCoroutinesuspendCancellableCoroutine取消支持不支持支持资源清理无invokeOnCancellation生产使用不推荐推荐
| 特性 | suspendCoroutine | suspendCoroutine |
|---|---|---|
| 取消支持 | 不支持 | 支持 |
| 资源清理 | 无 | invokeOnCancellation |
| 生产使用 | 不推荐 | 推荐 |
二、callbackFlow
2.1 概念
callbackFlow 用于将多次回调事件转换为 Flow 流。适用于事件监听、传感器数据、广播接收等持续产生数据的场景。
2.2 基本语法
fun eventFlow(): Flow<T> = callbackFlow {
// 设置监听器
val listener = object : EventListener {
override fun onEvent(event: T) {
trySend(event) // 非阻塞发送
}
}
registerListener(listener)
// 等待流结束,执行清理
awaitClose {
unregisterListener(listener)
}
}
2.3 实际应用示例
示例一:位置监听
fun LocationManager.locationFlow(
provider: String = LocationManager.GPS_PROVIDER,
minTime: Long = 1000,
minDistance: Float = 10f
): Flow<Location> = callbackFlow {
val listener = object : LocationListener {
override fun onLocationChanged(location: Location) {
trySend(location)
}
override fun onStatusChanged(provider: String?, status: Int, extras: Bundle?) {}
override fun onProviderEnabled(provider: String) {}
override fun onProviderDisabled(provider: String) {}
}
requestLocationUpdates(provider, minTime, minDistance, listener)
awaitClose {
removeUpdates(listener)
}
}
// 使用
locationManager.locationFlow()
.filter { it.accuracy < 10 }
.map { LocationData(it.latitude, it.longitude) }
.collect { location ->
updateUI(location)
}
示例二:BroadcastReceiver 事件流
fun Context.broadcastFlow(
vararg actions: String
): Flow<Intent> = callbackFlow {
val receiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
trySend(intent)
}
}
val filter = IntentFilter().apply {
actions.forEach { addAction(it) }
}
registerReceiver(receiver, filter)
awaitClose {
unregisterReceiver(receiver)
}
}
// 使用:监听网络状态变化
context.broadcastFlow(ConnectivityManager.CONNECTIVITY_ACTION)
.map { it.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false) }
.distinctUntilChanged()
.collect { isConnected ->
updateConnectionStatus(isConnected)
}
示例三:Firebase 实时数据
fun DatabaseReference.valueFlow(): Flow<DataSnapshot> = callbackFlow {
val listener = object : ValueEventListener {
override fun onDataChange(snapshot: DataSnapshot) {
trySend(snapshot)
}
override fun onCancelled(error: DatabaseError) {
close(error.toException())
}
}
addValueEventListener(listener)
awaitClose {
removeEventListener(listener)
}
}
2.4 关键要点
fun exampleFlow(): Flow<Int> = callbackFlow {
// 1. trySend 是非阻塞的
trySend(1) // 不挂起,立即返回
// 2. 可以安全地在任何线程调用
withContext(Dispatchers.IO) {
trySend(2)
}
// 3. 流被取消时执行清理
awaitClose {
cleanup()
}
}
三、channelFlow
3.1 概念
channelFlow 类似于 callbackFlow,但允许在协程上下文中生产数据,支持挂起操作,并具有背压控制能力。
3.2 基本语法
fun dataFlow(): Flow<T> = channelFlow {
// 可以使用挂起函数
val data = loadData() // 挂起操作
// send 是挂起函数,有背压控制
send(data)
// 可以启动子协程
launch {
repeat(10) {
send(produceItem(it))
}
}
}
3.3 实际应用示例
示例一:分页数据加载
fun Repository.pagedItemsFlow(
pageSize: Int = 20
): Flow<List<Item>> = channelFlow {
var page = 0
var hasMore = true
while (hasMore && isActive) { // isActive 检查流是否还在收集
val items = api.fetchItems(page, pageSize)
if (items.isEmpty()) {
hasMore = false
} else {
send(items)
page++
}
}
}
示例二:多数据源合并
fun mergedDataFlow(): Flow<Data> = channelFlow {
// 同时从数据库和网络获取数据
launch {
localDataSource.getAll().collect { send(it.copy(source = "local")) }
}
launch {
remoteDataSource.getAll().collect { send(it.copy(source = "remote")) }
}
// 两个源的数据会交替发送到流中
}
示例三:带状态的生产者
fun counterFlow(
initial: Int = 0,
delayMs: Long = 1000
): Flow<Int> = channelFlow {
var count = initial
while (isActive) {
send(count++)
delay(delayMs)
}
}
3.4 callbackFlow vs channelFlow
// callbackFlow - 使用 trySend(非阻塞)
fun callbackBasedFlow(): Flow<Int> = callbackFlow {
val listener = object : SomeListener {
override fun onEvent(value: Int) {
trySend(value) // 非阻塞,可能丢弃数据
}
}
registerListener(listener)
awaitClose { unregisterListener(listener) }
}
// channelFlow - 使用 send(挂起,有背压)
fun coroutineBasedFlow(): Flow<Int> = channelFlow {
repeat(100) {
send(it) // 挂起,等待消费者处理
delay(100)
}
}
| 特性 | callbackFlow | channelFlow |
|---|---|---|
| 发送方法 | trySend() | send() |
| 阻塞式 | 非阻塞 | 挂起函数 |
| 背压控制 | 缓冲区满时丢弃/挂起 | 自动背压 |
| 适用场景 | 回调、监听器 | 协程内生产 |
| 资源清理 | awaitClose() | 代码块结束自动清理 |
| 协程支持 | 有限 | 完整支持launch/async |
四、CompletableDeferred
4.1 概念
CompletableDeferred 是一个可外部控制的 Deferred,允许你在任意时刻手动完成它或设置异常。适用于需要在协程外部控制结果完成时机的场景。
4.2 基本语法
// 创建
val deferred = CompletableDeferred<String>()
// 完成方式
deferred.complete("结果") // 成功完成
deferred.completeExceptionally(Exception()) // 异常完成
// 等待结果
val result = deferred.await() // 挂起直到完成
// 状态检查
deferred.isCompleted // 是否已完成
deferred.isCancelled // 是否已取消
deferred.getCompleted() // 获取已完成的结果(非挂起)
4.3 实际应用示例
示例一:连接管理
class ConnectionManager {
private val connectionReady = CompletableDeferred<Connection>()
fun connect() {
// 异步建立连接
Thread {
try {
Thread.sleep(1000) // 模拟连接耗时
val connection = createConnection()
connectionReady.complete(connection)
} catch (e: Exception) {
connectionReady.completeExceptionally(e)
}
}.start()
}
suspend fun waitForConnection(): Connection {
return connectionReady.await()
}
fun disconnect() {
connectionReady.takeIf { !it.isCompleted }?.cancel()
}
}
// 使用
val manager = ConnectionManager()
manager.connect()
// 在其他地方等待连接
scope.launch {
val conn = manager.waitForConnection()
conn.sendData("Hello")
}
示例二:初始化同步
class DataRepository {
private val initialized = CompletableDeferred<Unit>()
private var cachedData: List<Data>? = null
suspend fun init() {
cachedData = loadFromDatabase()
initialized.complete(Unit)
}
suspend fun getData(): List<Data> {
initialized.await() // 确保初始化完成
return cachedData!!
}
suspend fun refresh() {
initialized.await()
cachedData = loadFromRemote()
}
}
// 使用
val repo = DataRepository()
// 先初始化
scope.launch { repo.init() }
// 其他协程可以安全调用
scope.launch {
repo.getData() // 会等待初始化完成
}
示例三:一次性事件
class EventNotifier<T> {
private val event = CompletableDeferred<T>()
suspend fun await(): T = event.await()
fun notify(value: T) {
event.complete(value)
}
fun fail(error: Throwable) {
event.completeExceptionally(error)
}
}
// 使用:等待用户点击
class DialogHelper {
private val clickEvent = CompletableDeferred<Boolean>()
fun show() {
AlertDialog.Builder(context)
.setTitle("确认")
.setPositiveButton("确定") { _, _ ->
clickEvent.complete(true)
}
.setNegativeButton("取消") { _, _ ->
clickEvent.complete(false)
}
.show()
}
suspend fun awaitResult(): Boolean = clickEvent.await()
}
示例四:竞态结果
suspend fun fetchFromFastest(
sources: List<DataSource>
): Data {
val result = CompletableDeferred<Data>()
// 同时向多个源请求
sources.map { source ->
CoroutineScope(Dispatchers.IO).launch {
try {
val data = source.fetch()
result.complete(data) // 第一个完成的生效
} catch (e: Exception) {
// 单个失败不影响其他源
}
}
}
return result.await()
}
4.4 Deferred vs CompletableDeferred
// Deferred - 由协程创建,结果由协程内部决定
val deferred1: Deferred<String> = async {
delay(1000)
"自动结果" // 协程内部返回
}
// CompletableDeferred - 手动控制完成
val deferred2 = CompletableDeferred<String>()
// 可以在外部控制
fun onComplete(value: String) {
deferred2.complete(value)
}
| 特性 | Deferred | CompletableDeferred |
|---|---|---|
| 创建方式 | async() | 构造函数 |
| 完成控制 | 协程内部自动 | 外部手动 |
| 灵活性 | 固定逻辑 | 高度灵活 |
| 典型场景 | 并发计算 | 外部事件协调 |
五、技术选型指南
5.1 决策流程图
开始
│
├─ 结果是单次还是多次?
│ │
│ ├─ 单次 ─────────────────────────────┐
│ │ │
│ │ 回调API? │
│ │ ├─ 是 → suspendCancellableCoroutine
│ │ └─ 需外部控制 → CompletableDeferred
│ │
│ └─ 多次 ─────────────────────────────┤
│ │
│ 回调/监听器API? │
│ ├─ 是 → callbackFlow │
│ └─ 协程内生产 → channelFlow │
│ │
└──────────────────────────────────────────┘
5.2 对比总结表
| 技术 | 结果类型 | 创建方式 | 取消支持 | 典型场景 |
|---|---|---|---|---|
| suspendCancellableCoroutine | 单次 | 挂起函数 | 支持 | 回调转挂起函数 |
| callbackFlow | 多次 | Flow | 支持 | 事件监听、传感器 |
| channelFlow | 多次 | Flow | 支持 | 协程内生产、背压 |
| CompletableDeferred | 单次 | Deferred | 支持 | 外部事件协调 |
5.3 选择速查
// ✅ 单次回调 → suspendCancellableCoroutine
suspend fun fetchUser(): User = suspendCancellableCoroutine { ... }
// ✅ 单次外部控制 → CompletableDeferred
class Connection { private val ready = CompletableDeferred<Unit>() }
// ✅ 多次事件监听 → callbackFlow
fun locationFlow(): Flow<Location> = callbackFlow { ... }
// ✅ 协程生产流 → channelFlow
fun pagedFlow(): Flow<Page> = channelFlow { ... }
六、最佳实践
6.1 异常处理
// suspendCancellableCoroutine
suspend fun safeApiCall(): Result<T> = suspendCancellableCoroutine { cont ->
api.call(
onSuccess = { cont.resume(Result.success(it)) },
onFailure = { cont.resume(Result.failure(it)) }
)
}
// callbackFlow - 使用 close 传递异常
fun dataFlow(): Flow<Data> = callbackFlow {
listener = object : Listener {
override fun onData(data: Data) { trySend(data) }
override fun onError(e: Throwable) { close(e) }
}
awaitClose { unregister(listener) }
}
6.2 取消处理
// suspendCancellableCoroutine - 必须处理取消
suspend fun download(): File = suspendCancellableCoroutine { cont ->
val call = httpClient.newCall(request)
cont.invokeOnCancellation { call.cancel() }
call.enqueue(callback)
}
// callbackFlow - awaitClose 处理清理
fun eventFlow(): Flow<Event> = callbackFlow {
val listener = createListener { trySend(it) }
register(listener)
awaitClose { unregister(listener) } // 流取消时自动调用
}
// channelFlow - 使用 isActive 检查
fun producerFlow(): Flow<Int> = channelFlow {
while (isActive) { // 检查是否被取消
send(produce())
}
}
6.3 线程安全
// CompletableDeferred 线程安全
class SharedResult {
private val result = CompletableDeferred<String>()
// 可以从任何线程调用
fun completeFromAnyThread(value: String) {
result.complete(value) // 线程安全
}
}