Kotlin 协程异步桥接技术详解

28 阅读6分钟

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生产使用不推荐推荐

特性suspendCoroutinesuspendCoroutine
取消支持不支持支持
资源清理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)
    }
}
特性callbackFlowchannelFlow
发送方法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)
}
特性DeferredCompletableDeferred
创建方式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)  // 线程安全
    }
}

参考资料