0%

suspendCoroutine和suspendCancellableCoroutine使用

1. suspendCoroutine作用及用法

suspendCoroutine 是 Kotlin 中用于挂起协程并与外部世界进行交互的函数之一。它的主要作用是允许你在协程中将异步非挂起的操作转化为挂起操作,从而可以方便地与协程上下文集成。具体来说,suspendCoroutine 的主要作用包括以下几点:

  1. 将回调式的异步操作转化为挂起操作:当你需要在协程中执行某些异步操作,例如调用回调函数,处理回调式 API,或等待某个事件发生时,suspendCoroutine 允许你将这些异步操作封装为挂起操作,使得你可以像调用普通的挂起函数一样调用它们,从而简化了异步编程的代码。
  2. 挂起当前协程:suspendCoroutine 在执行时会挂起当前协程,这意味着协程的执行会暂停,直到回调操作完成。这有助于防止阻塞线程,并允许其他协程在执行。
  3. 传递回调函数:suspendCoroutine 接受一个 Lambda 表达式,该 Lambda 表达式需要传递一个函数参数,通常是一个回调函数,该回调函数将在异步操作完成时被调用。在 Lambda 表达式内部,你可以使用 resume 来恢复协程的执行并传递结果,或者使用 resumeWithException 来传递异常,以便协程可以处理结果或错误。

suspendCoroutine案例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
suspend fun main() {  
withContext(Dispatchers.IO) {
val result = async { getResult() }.await()
println("result=$result")
}
}

suspend fun getResult() = suspendCoroutine<Boolean> {
val callback = object : Callback {
override fun onSuccess() {
it.resume(true)
}

override fun onFail() {
it.resumeWithException(Exception("fail"))
}

override fun onCancel() {
it.resume(false)
}
}
handleCallback(callback)
}

fun handleCallback(callback: Callback) {
callback.onFail()
}

interface Callback {
fun onSuccess()
fun onFail()
fun onCancel()
}

suspendCancellableCoroutine作用和用法

suspendCancellableCoroutine 是 Kotlin 中的一个函数,它允许你在一个协程中创建一个可取消的挂起操作,通常用于实现自定义的挂起函数或处理异步操作。基本功能和suspendCoroutine一样,只是多了一个支持监听外部协程取消和取消内部协程执行的能力。

suspendCancellableCoroutine案例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// 自定义的可取消挂起函数  
suspend fun customCancelableOperation(): String {
return suspendCancellableCoroutine { continuation ->
val job = GlobalScope.launch {
try {
// 模拟异步操作
delay(1000)
// 如果操作成功,恢复挂起操作并传递结果
continuation.resume("Operation completed successfully")
} catch (e: CancellationException) {
// 如果操作被取消,处理取消操作
continuation.resumeWithException(e)
} catch (e: Exception) {
// 如果操作失败,传递异常
continuation.resumeWithException(e)
}
}

// 当外部协程取消时,取消内部协程
continuation.invokeOnCancellation {
job.cancel()
}
}
}

fun main() = runBlocking {
val job = launch {
val result = customCancelableOperation()
println(result)
}

delay(500) // 等待一段时间
job.cancel() // 取消外部协程
job.join()
}

处理resumeWithException的通用方案

除了通过try catch的方式来捕获异常,也可以在launch协程的context中添加exceptionHandler来统一处理异常。 例如:

1
2
3
4
5
6
7
8
9
10
11
fun main() = runBlocking {  
val exceptionHandler = CoroutineExceptionHandler { _, exception ->
println("Caught exception: $exception")
}

val job = GlobalScope.launch(exceptionHandler) {
println(data)
}

job.join()
}

其他

在日常开发中,在协程上下文将异步非挂起的操作转化为挂起操作时遇到过多次回调完成的情况,导致一个continuation被多次resume,从而出现异常java.lang.IllegalStateException: Already resumed...

这时,可以通过使用suspendCancellableCoroutine并通过判断其状态决定是否需要真正resume来避免重复多次resume。

1
2
3
4
5
6
7
8
9
fun <T> Continuation<T>.safeResume(value: T) {  
if (this is CancellableContinuation) {
if (isActive) {
resume(value)
} else {
CapaLog.d("Continuation", "continuation is not active")
}
} else throw Exception("Must use suspendCancellableCoroutine instead of suspendCoroutine")
}