今天这个是送分题,但是仍然有很多人不清楚,今天就聊聊这个
比如:
interface UserApi {
@GET("user")
suspend fun getUser(): User
}
调用:
val user = api.getUser()
这里 Retrofit 并不是同步等待网络结果。
它只是把原来的异步流程转换成了 Kotlin 协程可以理解的挂起恢复流程。所以你不要再withContext 一下。
Retrofit 是怎么知道这是 suspend 方法?
Kotlin 的 suspend 函数在编译后,会增加一个隐藏参数:
Object getUser(
Continuation<? super User> continuation
)
也就是说:
suspend fun getUser(): User
实际上类似:
getUser(Continuation)
Retrofit 在解析接口方法时,会检查方法参数。
如果发现最后一个参数是:
Continuation
就认为:
这是 Kotlin suspend 方法。
Retrofit 内部如何处理 suspend 方法?
核心类:
HttpServiceMethod
Retrofit 根据不同返回类型创建不同的处理方式。
对于:
suspend fun getUser(): User
会创建:
SuspendForBody
它负责把:
Call<User>
转换成:
User
SuspendForBody 内部做了什么?
核心逻辑类似:
suspend fun <T> Call<T>.await(): T {
return suspendCancellableCoroutine { continuation ->
enqueue(object : Callback<T> {
override fun onResponse(
call: Call<T>,
response: Response<T>
) {
continuation.resume(
response.body()!!
)
}
override fun onFailure(
call: Call<T>,
error: Throwable
) {
continuation.resumeWithException(error)
}
})
}
}
它主要完成三件事:
- 发起异步请求
enqueue()
- 保存当前协程执行状态
Continuation
- 请求完成后恢复协程
成功:
continuation.resume()
失败:
continuation.resumeWithException()
这里通过 suspendCancellableCoroutine 把原来的callback 转成 suspend
这里的 await 是 Kotlin 协程提供的吗?
不是。
这里的:
await()
只是 Retrofit 定义的扩展函数。
它只是一个名字。
真正关键的是:
suspendCancellableCoroutine
以及:
Continuation.resume()
那 Kotlin 协程里的 await 是什么?
Kotlin 协程也有一个 await:
val result = async {
request()
}.await()
这个属于:
Deferred.await()
作用:
等待另一个协程任务完成。
而 Retrofit 的:
Call.await()
作用:
把网络请求结果转换成协程结果。
两个不是同一个东西。
Retrofit suspend 底层是不是阻塞线程?
不是。
它不会:
线程等待网络
而是:
调用接口
↓
协程挂起
↓
线程释放
↓
OkHttp 执行网络请求
↓
结果回来
↓
Continuation.resume()
↓
协程继续执行
等待的是:
异步结果
Retrofit suspend 会切换 Dispatchers.IO 吗?
Retrofit 不负责线程调度。
网络线程:
OkHttp Dispatcher
协程恢复:
Coroutine Dispatcher
两个职责不同。
那异常为什么可以直接 try catch?
因为 Retrofit 把 Callback 的失败转换成协程异常。
以前:
onFailure()
异常在回调里。
现在:
continuation.resumeWithException(error)
协程状态机收到异常后:
等价于:
throw error
所以:
try {
api.getUser()
} catch(e:Exception){
}
可以捕获。
总结 Retrofit suspend 原理
Retrofit 的 suspend 支持,本质是通过动态代理识别 suspend 方法,利用 Continuation 将 OkHttp 的异步结果转换成协程挂起恢复流程,让 Callback 风格的 API 变成 suspend 风格的 API。