先看举例的代码:
一个 Retrofit API 接口 VideoApi,其方法是 suspend 方法。
interface VideoApi {
@GET("https://live.{env}.shopee.{cid}/api/v1/homepage/replay")
suspend fun getVideoList(
@Path("env") env: String = EnvConstants.NETWORK_ENV,
@Path("cid") cid: String = EnvConstants.NETWORK_CID,
@Query("offset") offset: Int = 0,
@Query("limit") limit: Int = 10
): VideoListResponse
}
一个继承了 Paging3 的 RemoteMediator 的类 VideoRemoteMediator 。其 load() 方法是一个 suspend 方法,这个方法体里调用的 videoApi.getVideoList() 也是 suspend 方法。
class VideoRemoteMediator(
private val videoDatabase: VideoDatabase,
private val videoApi: VideoApi,
private val remoteKeyName: String
) : RemoteMediator<Int, VideoInfo>() {
...
override suspend fun load(
loadType: LoadType,
state: PagingState<Int, VideoInfo>
): MediatorResult {
...
val result = videoApi.getVideoList(offset = loadKey, limit = limit)
...
val nextKey = if (result.videoListData.hasMore) (loadKey + videoList.size) else null
...
return MediatorResult.Success(endOfPaginationReached = videoList.isEmpty())
}
}
1、load() 方法在主线程的协程里执行,执行到 videoApi.getVideoList()。
2、videoApi.getVideoList() 方法由 Retrofit 写的动态代理实现。
3、接着执行 SuspendForBody 的父类 HttpServiceMethod 的 invoke() 方法。
4、执行 SuspendForBody 的 adapt 方法,Object[] args 参数为 videoApi.getVideoList() 的所有入参,包括最后一个 Continuation 参数。
5、 进入 KotlinExtension.await() 方法,这是一个用 kotlin 编写的 Call 的扩展 suspend 方法。
suspend fun <T : Any> Call<T>.await(): T {
return suspendCancellableCoroutine { continuation ->
continuation.invokeOnCancellation {
cancel()
}
enqueue(object : Callback<T> {
override fun onResponse(call: Call<T>, response: Response<T>) {
if (response.isSuccessful) {
val body = response.body()
if (body == null) {
val invocation = call.request().tag(Invocation::class.java)!!
val method = invocation.method()
val e = KotlinNullPointerException("Response from " +
method.declaringClass.name +
'.' +
method.name +
" was null but response body type was declared as non-null")
continuation.resumeWithException(e)
} else {
continuation.resume(body)
}
} else {
continuation.resumeWithException(HttpException(response))
}
}
override fun onFailure(call: Call<T>, t: Throwable) {
continuation.resumeWithException(t)
}
})
}
}
编译后,其第一个参数是 Call,第二个参数是 Continuation。
这个方法使用了 内联 suspend 方法 suspendCancellableCoroutine() 来实现。
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() 又使用了 内联 suspend 方法 suspendCoroutineUninterceptedOrReturn() 来实现。
public suspend inline fun <T> suspendCoroutineUninterceptedOrReturn(crossinline block: (Continuation<T>) -> Any?): T {
contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
throw NotImplementedError("Implementation of suspendCoroutineUninterceptedOrReturn is intrinsic")
}
suspendCoroutineUninterceptedOrReturn() 方法在编译期实现(参考:https://blog.csdn.net/hegan2010/article/details/117789735),相当于执行:
return block(continuation)
其中 continuation 为 suspend 方法的隐藏的最后一个 Continuation 参数。
最后,suspend fun <T : Any> Call<T>.await(): T 方法等效于如下:
fun <T : Any> await(call: Call<T>, continuation: Continuation<T>): T {
val cancellable =
CancellableContinuationImpl(continuation.intercepted(), resumeMode = MODE_CANCELLABLE)
cancellable.initCancellability()
cancellable.invokeOnCancellation {
call.cancel()
}
call.enqueue(object : Callback<T> {
override fun onResponse(call: Call<T>, response: Response<T>) {
if (response.isSuccessful) {
val body = response.body()
if (body == null) {
val invocation = call.request().tag(Invocation::class.java)!!
val method = invocation.method()
val e = KotlinNullPointerException(
"Response from " +
method.declaringClass.name +
'.' +
method.name +
" was null but response body type was declared as non-null"
)
cancellable.resumeWithException(e)
} else {
cancellable.resume(body)
}
} else {
cancellable.resumeWithException(HttpException(response))
}
}
override fun onFailure(call: Call<T>, t: Throwable) {
cancellable.resumeWithException(t)
}
})
return cancellable.getResult()
}
这里会执行 call.enqueue() 方法将网络请求入队。
然后返回 COROUTINE_SUSPENDED 将 videoApi.getVideoList() 方法挂起。
转载请说明出处:https://blog.csdn.net/hegan2010/article/details/117902011
6、当 Retrofit 在网络请求线程中执行完成后,通过 continuation.resume() 方法,将 videoApi.getVideoList() 方法从主线程恢复执行,并传递执行结果。
注:此 continuation 为 KotlinExtension.await() 方法里创建的 CancellableContinuationImpl 。
7、CancellableContinuationImpl.resume() 方法会进入 DispatchedTask.dispatch() 扩展方法(CancellableContinuationImpl 继承了 DispatchedTask)。使用 CancellableContinuationImpl 代理的 delegate(Continuation) 的 dispatcher(CoroutineDispatcher) 和 context(CoroutineContext) 将其派发到 dispatcher 的线程池中进行恢复执行。
8、此 dispatcher 为 Dispatcher.Main.immediate(HandlerContext),其将 CancellableContinuationImpl 通过 post 稍后执行。
注:CancellableContinuationImpl 继承了 DispatchedTask,而 DispatchedTask 实现了 Runnable 接口。
9、回到主线程,执行 CancellableContinuationImpl 父类 DispatchedTask 实现的 run() 方法。
取出 CancellableContinuationImpl 的 delegate(DispatchedContinuation) 的 continuation(ContinuationImpl)。
这个 continuation 是在 VideoRemoteMediator.load() 方法在调用 VideoApi.getVideoList() 前创建的匿名内部类实例(编译器插入代码实现的,参考:https://blog.csdn.net/hegan2010/article/details/117796291)。
VideoRemoteMediator.load() 方法在编译期间插入代码后类似如下(LocalContinuationImpl 表示 continuation 所属的类。这里只考虑 load() 方法体里只调用了 videoApi.getVideoList() 这一个 suspend 方法):
override fun load(
loadType: LoadType,
state: PagingState<Int, VideoInfo>,
continuation : Continuation<MediatorResult>
): Object {
class LocalContinuationImpl(completion: Continuation<Any?>?) :
ContinuationImpl(completion) {
// 保存 suspend 方法执行结果
var result: Any? = null
// 状态机状态标记
var label = 0
// 用于保存局部临时变量
var L$0 : Any? = null
...
var L$x : Any? = null
protected override fun invokeSuspend(lastResult: Any): Any? {
result = lastResult
label = label or Int.MIN_VALUE
return load(null, null, this) // 恢复执行时重新调用 load() 方法,除了最后一个 continuation 参数,其他的参数都是 null 值。
}
}
var localContinuation: LocalContinuationImpl
block_label@{
if (continuation is LocalContinuationImpl) { // 说明这是恢复执行 load() 方法
localContinuation = continuation
if (localContinuation.label and Int.MIN_VALUE != 0) {
localContinuation.label -= Int.MIN_VALUE
break@block_label
}
}
localContinuation = LocalContinuationImpl(continuation) // 说明这是第一次执行 load() 方法
}
val lastResult = localContinuation.result
val state = COROUTINE_SUSPENDED
val newResult Any?
when (localContinuation.label) {
0 -> {
... // 执行 videoApi.getVideoList() 方法之前的代码
localContinuation.label = 1 // 设置下一次恢复执行的状态
newResult = videoApi.getVideoList(offset = loadKey, limit = limit,continuation = localContinuation)
if (newResult == state /*COROUTINE_SUSPENDED*/) {
return state // videoApi.getVideoList() 返回 COROUTINE_SUSPENDED,说明需要挂起 load() 方法的执行
}
// videoApi.getVideoList() 立即返回结果值,说明不需要挂起 load() 方法的执行,退出 when 后继续执行 videoApi.getVideoList() 方法之后的代码,返回最终结果。
}
1 -> {
... // 恢复 localContinuation 保存的临时变量 L$0, ..., L$x
newResult = lastResult;
}
else -> throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine")
}
... // 执行 videoApi.getVideoList() 方法之后的代码
MediatorResult finalResult = ...
return finalResult
}
10、然后执行上述 continuation(ContinuationImpl) 的 resume() 方法。
进而执行父类 BaseContinuationImpl 实现的 resumeWith() 方法。
看注释:此循环在 current.resumeWith(param) 中展开递归,以使恢复时的堆栈跟踪更清晰、更短。
这里会调用 invokeSuspend() 方法。
invokeSuspend() 方法会恢复执行 VideoRemoteMediator.load() 方法。
class LocalContinuationImpl(completion: Continuation<Any?>?) :
ContinuationImpl(completion) {
protected override fun invokeSuspend(lastResult: Any): Any? {
...
return load(null, null, this)
}
}
继续执行后续代码。。。