1.5-协程基础与关键知识:连接线程的世界-回调型 API 协作

线程 API 转换成挂起函数:suspendCoroutine

在实际项目中即使已经使用协程了,可是要完全避免跟传统的线程 API 交互并不容易,大型项目一般都会有比较多的老代码或外部库没有用协程,使用的还是回调的写法。那么就很有必要知道怎么让协程和线程 API 的回调交互。

协程有一个专用的函数 suspendCoroutine,它是一个挂起函数,在它里面调用传统的回调式函数,就能把回调式的函数转换成挂起函数

lifecycleScope.launch {
	val contributors = callbackToSuspend()
	showContributors(contributors)
}

private suspend fun callbackToSuspend() = suspendCoroutine {
	gitHub.contributorsCall("square", "retrofit")
		.enqueue(object : Callback<List<Contributor>> {
			override fun onResponse(
				call: Call<List<Contributor>>, response: Response<List<Contributor>> {
				// 将结果返回
				it.resume(response.body()!!)
			}
			
			override fun onFailure(call: Call<List<Contributor>>) {
				// 发生异常时让 suspendCoroutine 立即结束并抛出这个异常
				it.resumeWithException(t)
			}
		})
	}
}

使用 suspendCoroutine 包裹的回调式代码需要调用 suspendCoroutine 提供的 continuation.resume 和 continuation.resumeWithException 分别处理正常返回结果和异常的情况

支持取消的 suspendCoroutine:suspendCancellableCoroutine

协程还提供了一个类似的函数 suspendCancellableCoroutine,和 suspendCoroutine 的区别是它支持取消

private suspend fun callbackToCancellableSuspend() = suspendCancellableCoroutine {
	it.invokeOnCancellation {
		// 协程被取消,处理协程被取消时要做的一些收尾工作清理现场
	}
	gitHub.contributorsCall("square", "retrofit")
		.enqueue(object : Callback<List<Contributor>> {
			override fun onResponse(
				call: Call<List<Contributor>>, response: Response<List<Contributor>> {
				// 将结果返回
				it.resume(response.body()!!)
			}
			
			override fun onFailure(call: Call<List<Contributor>>) {
				// 发生异常时让 suspendCoroutine 立即结束并抛出这个异常
				it.resumeWithException(t)
			}
		})
	}
}

使用 suspendCancellableCoroutine 还可以注册取消的回调,使用 cancellableContinuation.invokeOnCancellation 处理协程被取消时的收尾清理工作

我们用一个例子来说明 suspendCoroutine 和 suspendCancellableCoroutine 的区别:

val job = lifecycleScope.launch {
	// 假设 callbackToSuspend 会在延时 2s 后继续执行
	try {
		val contributors = callbackToSuspend()
		// val contributors = callbackToCancellableSuspend()
		showContributors(contributors)
	} catch (e: Exception) {
		textView.text = e.message
	}
}
lifecycleScope.launch {
	delay(200)
	job.cancel() // 200ms 后取消协程
}

假设 callbackToSuspend 函数是使用 suspendCoroutine 包起来的回调代码,会在 2s 后返回结果;协程 200ms 后被取消了,但是里面的代码是不配合的,因为协程的取消本身就是一个状态标记,2s 后还是会继续执行代码。

而如果用 suspendCancellableCoroutine 在 200ms 后会正常取消协程,会在 try-catch 抛出 CancellableException 异常,不会在继续执行后续代码。

我们一般在项目中都使用能支持取消的 suspendCancellableCoroutine,除非特殊需求需要启动后协程取消了也得继续执行才用 suspendCoroutine

总结

  • 将线程 API 的回调式代码用 suspendCoroutine 或 suspendCancellableCoroutine 包住,就能实现将回调式代码转换为挂起函数在协程执行,需要调用提供的 continuation.resume 和 continuation.resumeWithException 分别处理正常返回结果和异常的情况

  • suspendCancellableCoroutine 和 suspendCoroutine 的区别是它支持取消和注册协程取消回调;我们一般在项目中都使用能支持取消的 suspendCancellableCoroutine,除非特殊需求需要启动后协程取消了也得继续执行才用 suspendCoroutine

相关推荐

  1. Kotlin 简化

    2024-07-15 05:02:02       22 阅读
  2. 使用 Lua 处理异步函数

    2024-07-15 05:02:02       112 阅读
  3. FREERTOS

    2024-07-15 05:02:02       32 阅读
  4. python中线

    2024-07-15 05:02:02       31 阅读
  5. Golang 通道

    2024-07-15 05:02:02       58 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-15 05:02:02       67 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-15 05:02:02       72 阅读
  3. 在Django里面运行非项目文件

    2024-07-15 05:02:02       58 阅读
  4. Python语言-面向对象

    2024-07-15 05:02:02       69 阅读

热门阅读

  1. 等保测评助力网络安全治理现代化

    2024-07-15 05:02:02       20 阅读
  2. 去中心化预言机是什么

    2024-07-15 05:02:02       32 阅读
  3. Electron 进程间通信

    2024-07-15 05:02:02       26 阅读
  4. Windows 快捷键汇总

    2024-07-15 05:02:02       29 阅读
  5. HTTP 协议格式与 Fiddler 工具的用法

    2024-07-15 05:02:02       25 阅读
  6. 深入解析PHP框架:Symfony框架详解与应用

    2024-07-15 05:02:02       22 阅读
  7. UniApp:跨平台移动应用开发的终极指南

    2024-07-15 05:02:02       25 阅读
  8. LeetCode 算法:子集 c++

    2024-07-15 05:02:02       21 阅读
  9. 赫夫曼编码-C语言

    2024-07-15 05:02:02       21 阅读
  10. WEB安全-文件上传漏洞

    2024-07-15 05:02:02       16 阅读
  11. 线段树最大与最小值模板

    2024-07-15 05:02:02       19 阅读