跳至主要內容

包装回调

guodongAndroid大约 2 分钟

处理异步代码执行通常涉及实现某种回调机制。例如一个异步网络调用,你可能想有 onSuccessonFailure 两个回调,这样你就可以适当地处理这两个情况。

这种代码通常会变得相当复杂和难以阅读。幸运的是,协程提供了一种包装回调的方法,以通过包含在协程库中的suspendCancellableCoroutine 挂起函数向调用者隐藏异步代码处理的复杂性。它捕获当前续体实例并挂起当前运行的协程。

Continuation 对象提供了两个函数可以让你恢复协程的执行。调用 resume 函数恢复协程的执行并返回一个值,而 resumeWithException() 会在最后一个挂起点之后重新抛出异常。

恢复是通过在挂起函数内计划将来对其中一个 Continuation<T> 方法的调用来完成的。

看看一个简单的长时间运行任务的示例,其中包含处理结果的回调。你将在协程中包装该回调,并显著简化工作。在你的入门项目中打开 CallbackWrapping.kt 文件。你应该在那里找到一些预先写好的代码——一个模拟长时间运行任务的函数,以及你将包装的 AsyncCallback。为了完成示例将以下代码添加到 main() 函数中:

fun main() {
  runBlocking {
    try {
      val data = getDataAsync()
	  println("Data received: $data")
    } catch (e: Exception) {
      println("Caught ${e.javaClass.simpleName}")
    }
  } 
}

main() 函数下面添加以下代码:

// Callback Wrapping using Coroutine
suspend fun getDataAsync(): String {
  return suspendCancellableCoroutine { continuation ->
    getData(object : AsyncCallback {
      override fun onSuccess(result: String) {
        continuation.resume(result)
      }
      override fun onError(e: Exception) {
        continuation.resumeWithException(e)
      }
	})
  }
}

输出:

  • getData() 函数中的 triggerError 字段设置为 false 时:
received: [Beep.Boop.Beep]
  • getData() 函数中的 triggerError 字段设置为 true 时:
Caught IOException

在上述示例,你有效地在使用回调的异步代码和使用协程的异步代码之间建立了桥梁。通过这样做,你得到了一个漂亮的、可读的代码,该代码看起来是同步的,但实际上仍然在背后进行异步工作。