suspend 关键字与可暂停函数
1. 什么是 suspend 函数?
suspend 是 Kotlin 协程中的核心关键字,用于标记一个函数为可暂停函数。这类函数可以在不阻塞线程的情况下暂停协程的执行,并在合适的时机恢复。
特点:
- 非阻塞:
suspend函数不会阻塞调用它的线程,而是挂起协程,释放线程资源。 - 协程上下文:只能在协程或其他
suspend函数中调用。 - 异步逻辑同步化:通过
suspend函数,可以用同步代码风格编写异步逻辑。
2. 基本语法
suspend fun fetchData(): String {
delay(1000) // 模拟耗时操作(delay 本身是 suspend 函数)
return "Data loaded"
}
3. 工作原理
suspend 函数通过 Kotlin 编译器的 CPS(Continuation-Passing Style)转换实现:
- 编译器将
suspend函数转换为状态机,每个挂起点(如delay)对应一个状态。 - 当函数执行到挂起点时,保存当前状态并暂停协程。
- 挂起结束后,通过
Continuation对象恢复执行。
4. 使用场景
- IO 操作:网络请求、文件读写。
- 耗时计算:数据库查询、复杂运算。
- 与其他
suspend函数交互:例如调用Retrofit的挂起接口。
5. 示例:网络请求
suspend fun getUserProfile(userId: String): UserProfile {
return withContext(Dispatchers.IO) { // 切换到 IO 线程池
apiService.fetchUserProfile(userId) // 假设是 Retrofit 的 suspend 函数
}
}
6. 注意事项
- 避免阻塞操作:
suspend函数内部不应包含Thread.sleep()等阻塞调用。 - 线程切换:默认在调用方的协程上下文中执行,需通过
withContext显式切换线程(如Dispatchers.IO)。 - 异常处理:使用
try-catch或在协程构建器(如launch/async)中处理异常。
7. 常见问题
Q: 为什么普通函数不能调用 suspend 函数?
A: 普通函数不具备挂起能力,必须通过协程构建器(如 launch)或另一个 suspend 函数调用。
Q: suspend 函数是否等同于后台线程?
A: 不是。挂起是协程的行为,与线程无关。协程可能运行在单线程(如 Dispatchers.Main)或多个线程上。
关键点总结
suspend函数是协程的基石,用于编写非阻塞代码。- 挂起和恢复由协程框架管理,开发者只需关注业务逻辑。
- 结合
CoroutineScope和调度器(如Dispatchers)实现高效并发。
