suspendCoroutine() 方法 和 suspendCancellableCoroutine() 方法都可以将异步转为协程。
通过 continuation.resume() 传递结果,continuation.resumeWithException() 传递异常。
public suspend inline fun <T> suspendCoroutine(crossinline block: (Continuation<T>) -> Unit): T {
contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
return suspendCoroutineUninterceptedOrReturn { c: Continuation<T> ->
val safe = SafeContinuation(c.intercepted())
block(safe)
safe.getOrThrow()
}
}
public suspend inline fun <T> suspendCancellableCoroutine(crossinline block: (CancellableContinuation<T>) -> Unit): T =
suspendCoroutineUninterceptedOrReturn { uCont ->
val cancellable = CancellableContinuationImpl(uCont.intercepted(), resumeMode = MODE_CANCELLABLE)
/*
* For non-atomic cancellation we setup parent-child relationship immediately
* in case when `block` blocks the current thread (e.g. Rx2 with trampoline scheduler), but
* properly supports cancellation.
*/
cancellable.initCancellability()
block(cancellable)
cancellable.getResult()
}
区别,suspendCancellableCoroutine()
可以在 cancelled 时进行响应处理,比如关闭资源;
可以在 resume 时却发现已经 cancelled 时,关闭返回的资源。
suspend fun getResultResourceSuspend(): ResultResource =
suspendCancellableCoroutine { continuation ->
val parentResource = ParentResource() // Open parent resource
continuation.invokeOnCancellation { cause ->
parentResource.close() // Ensures parent resource is closed on cancellation
}
parentResource.getResultResourceAsync(object : ParentResource.Callback {
override fun onSuccess(resultResource: ResultResource) {
continuation.resume(resultResource) { cause ->
resultResource.close() // Close sub resource on cancellation
}
if (!continuation.isCancelled) parentResource.close()
}
override fun onFailure(cause: Throwable) {
continuation.resumeWithException(cause)
if (!continuation.isCancelled) parentResource.close()
}
})
}
使用举例:
@RequiresApi(Build.VERSION_CODES.O)
object CallbackToCoroutineTest {
fun printMsg(msg: String) = println("${LocalDateTime.now()} ${Thread.currentThread()} : $msg")
fun printErr(msg: String) = println("${LocalDateTime.now()} ${Thread.currentThread()} !!! $msg")
abstract class Resource : Closeable {
init {
printMsg("open ${toString()}")
}
override fun close() {
printMsg("close ${toString()}")
}
override fun toString(): String {
return "${javaClass.simpleName}@${hashCode()}"
}
}
class ParentResource : Resource() {
interface Callback {
fun onSuccess(resultResource: ResultResource)
fun onFailure(cause: Throwable)
}
fun getResultResourceAsync(callback: Callback) {
thread {
Thread.sleep(1000)
when {
System.currentTimeMillis().toInt() and 1 == 1 -> {
callback.onSuccess(ResultResource())
}
else -> {
callback.onFailure(RuntimeException("failure"))
}
}
}
}
}
class ResultResource : Resource()
@JvmStatic
fun main(args: Array<String>) {
println()
val parentResource = ParentResource()
parentResource.getResultResourceAsync(object : ParentResource.Callback {
override fun onSuccess(resultResource: ResultResource) {
printMsg("callback onSuccess: $resultResource")
resultResource.close()
parentResource.close()
}
override fun onFailure(cause: Throwable) {
printErr("callback onFailure: $cause")
parentResource.close()
}
})
runBlocking {
val job: Job = launch {
try {
val resultResource = getResultResourceSuspend()
printMsg("coroutine success: $resultResource")
resultResource.close()
} catch (e: Throwable) {
printErr("coroutine failed: $e")
}
}
delay(100)
//job.cancel()
}
Thread.sleep(2000)
println()
}
suspend fun getResultResourceSuspend(): ResultResource =
suspendCancellableCoroutine { continuation ->
val parentResource = ParentResource() // Open parent resource
continuation.invokeOnCancellation { cause ->
parentResource.close() // Ensures parent resource is closed on cancellation
printErr("****** invokeOnCancellation $cause")
}
parentResource.getResultResourceAsync(object : ParentResource.Callback {
override fun onSuccess(resultResource: ResultResource) {
continuation.resume(resultResource) { cause ->
printErr("****** resume onCancellation $cause")
resultResource.close() // Close result resource on cancellation
}
if (!continuation.isCancelled) parentResource.close()
}
override fun onFailure(cause: Throwable) {
continuation.resumeWithException(cause)
if (!continuation.isCancelled) parentResource.close()
}
})
}
}