Kotlin圖文并茂講解續(xù)體與續(xù)體攔截器和調(diào)度器
一.Continuation
Continuation接口是協(xié)程中最核心的接口,代表著掛起點(diǎn)之后的續(xù)體,代碼如下:
public interface Continuation<in T> {
// 續(xù)體的上下文
public val context: CoroutineContext
// 該方法用于恢復(fù)續(xù)體的執(zhí)行
// result為掛起點(diǎn)執(zhí)行完成的返回值,T為返回值的類(lèi)型
public fun resumeWith(result: Result<T>)
}Continuation圖解

二.ContinuationInterceptor
ContinuationInterceptor接口繼承自Element接口,是協(xié)程中的續(xù)體攔截器,代碼如下:
public interface ContinuationInterceptor : CoroutineContext.Element {
// 攔截器的Key
companion object Key : CoroutineContext.Key<ContinuationInterceptor>
// 攔截器對(duì)續(xù)體進(jìn)行攔截時(shí)會(huì)調(diào)用該方法,并對(duì)continuation進(jìn)行緩存
// 攔截判斷:根據(jù)傳入的continuation對(duì)象與返回的continuation對(duì)象是否相同
public fun <T> interceptContinuation(continuation: Continuation<T>): Continuation<T>
// 當(dāng)interceptContinuation方法攔截的協(xié)程執(zhí)行完畢后,會(huì)調(diào)用該方法
public fun releaseInterceptedContinuation(continuation: Continuation<*>) {
/* do nothing by default */
}
// get方法多態(tài)實(shí)現(xiàn)
public override operator fun <E : CoroutineContext.Element> get(key: CoroutineContext.Key<E>): E? {
@OptIn(ExperimentalStdlibApi::class)
if (key is AbstractCoroutineContextKey<*, *>) {
@Suppress("UNCHECKED_CAST")
return if (key.isSubKey(this.key)) key.tryCast(this) as? E else null
}
@Suppress("UNCHECKED_CAST")
return if (ContinuationInterceptor === key) this as E else null
}
// minusKey方法多態(tài)實(shí)現(xiàn)
public override fun minusKey(key: CoroutineContext.Key<*>): CoroutineContext {
@OptIn(ExperimentalStdlibApi::class)
if (key is AbstractCoroutineContextKey<*, *>) {
return if (key.isSubKey(this.key) && key.tryCast(this) != null) EmptyCoroutineContext else this
}
return if (ContinuationInterceptor === key) EmptyCoroutineContext else this
}
}三.CoroutineDispatcher
CoroutineDispatcher類(lèi)繼承自AbstractCoroutineContextElement類(lèi),實(shí)現(xiàn)了ContinuationInterceptor接口,是協(xié)程調(diào)度器的基類(lèi),代碼如下:
public abstract class CoroutineDispatcher :
AbstractCoroutineContextElement(ContinuationInterceptor), ContinuationInterceptor {
// ContinuationInterceptor的多態(tài)實(shí)現(xiàn),調(diào)度器本質(zhì)上就是攔截器
@ExperimentalStdlibApi
public companion object Key : AbstractCoroutineContextKey<ContinuationInterceptor, CoroutineDispatcher>(
ContinuationInterceptor,
{ it as? CoroutineDispatcher })
// 用于判斷調(diào)度器是否要調(diào)用dispatch方法進(jìn)行調(diào)度,默認(rèn)為true
public open fun isDispatchNeeded(context: CoroutineContext): Boolean = true
// 調(diào)度的核心方法,在這里進(jìn)行調(diào)度,執(zhí)行block
public abstract fun dispatch(context: CoroutineContext, block: Runnable)
// 如果調(diào)度是由Yield方法觸發(fā)的,默認(rèn)通過(guò)dispatch方法實(shí)現(xiàn)
@InternalCoroutinesApi
public open fun dispatchYield(context: CoroutineContext, block: Runnable): Unit = dispatch(context, block)
// ContinuationInterceptor接口的方法,將續(xù)體包裹成DispatchedContinuation,并傳入當(dāng)前調(diào)度器
public final override fun <T> interceptContinuation(continuation: Continuation<T>): Continuation<T> =
DispatchedContinuation(this, continuation)
// 釋放父協(xié)程與子協(xié)程的關(guān)聯(lián)。
@InternalCoroutinesApi
public override fun releaseInterceptedContinuation(continuation: Continuation<*>) {
(continuation as DispatchedContinuation<*>).reusableCancellableContinuation?.detachChild()
}
// 重載了"+"操作,直接返回others
// 因?yàn)閮蓚€(gè)調(diào)度器相加沒(méi)有意義,同一個(gè)上下文中只能有一個(gè)調(diào)度器
// 如果需要加的是調(diào)度器對(duì)象,則直接替換成最新的,因此直接返回
public operator fun plus(other: CoroutineDispatcher): CoroutineDispatcher = other
override fun toString(): String = "$classSimpleName@$hexAddress"
}四.EventLoop
EventLoop類(lèi)繼承自CoroutineDispatcher類(lèi),用于協(xié)程中任務(wù)的分發(fā)執(zhí)行,只在runBlocking方法中和Dispatchers.Unconfined調(diào)度器中使用。與Handler中的Looper類(lèi)似,在創(chuàng)建后會(huì)存儲(chǔ)在當(dāng)前線程的ThreadLocal中。EventLoop本身不支持延時(shí)執(zhí)行任務(wù),如果需要可以自行繼承EventLoop并實(shí)現(xiàn)Delay接口,EventLoop中預(yù)留了一部分變量和方法用于延時(shí)需求的擴(kuò)展。
為什么協(xié)程需要EventLoop呢?協(xié)程的本質(zhì)是續(xù)體傳遞,而續(xù)體傳遞的本質(zhì)是回調(diào),假設(shè)在Dispatchers.Unconfined調(diào)度下,要連續(xù)執(zhí)行多個(gè)suspend方法,就會(huì)有多個(gè)續(xù)體傳遞,假設(shè)suspend方法達(dá)到一定數(shù)量后,就會(huì)造成StackOverflow,進(jìn)而引起崩潰。同樣的,我們知道調(diào)用runBlocking會(huì)阻塞當(dāng)前線程,而runBlocking阻塞的原理就是執(zhí)行“死循環(huán)”,因此需要在循環(huán)中做任務(wù)的分發(fā),去執(zhí)行內(nèi)部協(xié)程在Dispatchers.Unconfined調(diào)度器下加入的任務(wù)。
EventLoop代碼如下:
internal abstract class EventLoop : CoroutineDispatcher() {
// 用于記錄使用當(dāng)前EventLoop的runBlocking方法和Dispatchers.Unconfined調(diào)度器的數(shù)量
private var useCount = 0L
// 表示當(dāng)前的EventLoop是否被暴露給其他的線程
// runBlocking會(huì)將EventLoop暴露給其他線程
// 因此,當(dāng)runBlocking使用時(shí),shared必須為true
private var shared = false
// Dispatchers.Unconfined調(diào)度器的任務(wù)執(zhí)行隊(duì)列
private var unconfinedQueue: ArrayQueue<DispatchedTask<*>>? = null
// 處理任務(wù)隊(duì)列的下一個(gè)任務(wù),該方法只能在EventLoop所在的線程調(diào)用
// 返回值<=0,說(shuō)明立刻執(zhí)行下一個(gè)任務(wù)
// 返回值>0,說(shuō)明等待這段時(shí)間后,執(zhí)行下一個(gè)任務(wù)
// 返回值為L(zhǎng)ong.MAX_VALUE,說(shuō)明隊(duì)列里沒(méi)有任務(wù)了
public open fun processNextEvent(): Long {
if (!processUnconfinedEvent()) return Long.MAX_VALUE
return 0
}
// 隊(duì)列是否為空
protected open val isEmpty: Boolean get() = isUnconfinedQueueEmpty
// 下一個(gè)任務(wù)多長(zhǎng)時(shí)間后執(zhí)行
protected open val nextTime: Long
get() {
val queue = unconfinedQueue ?: return Long.MAX_VALUE
return if (queue.isEmpty) Long.MAX_VALUE else 0L
}
// 任務(wù)的核心處理方法
public fun processUnconfinedEvent(): Boolean {
// 若隊(duì)列為空,則返回
val queue = unconfinedQueue ?: return false
// 從隊(duì)首取出一個(gè)任務(wù),如果為空,則返回
val task = queue.removeFirstOrNull() ?: return false
// 執(zhí)行
task.run()
return true
}
// 表示當(dāng)前EventLoop是否可以在協(xié)程上下文中被調(diào)用
// EventLoop本質(zhì)上也是協(xié)程上下文
// 如果EventLoop在runBlocking方法中使用,必須返回true
public open fun shouldBeProcessedFromContext(): Boolean = false
// 向隊(duì)列中添加一個(gè)任務(wù)
public fun dispatchUnconfined(task: DispatchedTask<*>) {
// 若隊(duì)列為空,則創(chuàng)建一個(gè)新的隊(duì)列
val queue = unconfinedQueue ?:
ArrayQueue<DispatchedTask<*>>().also { unconfinedQueue = it }
queue.addLast(task)
}
// EventLoop當(dāng)前是否還在被使用
public val isActive: Boolean
get() = useCount > 0
// EventLoop當(dāng)前是否還在被Unconfined調(diào)度器使用
public val isUnconfinedLoopActive: Boolean
get() = useCount >= delta(unconfined = true)
// 判斷隊(duì)列是否為空
public val isUnconfinedQueueEmpty: Boolean
get() = unconfinedQueue?.isEmpty ?: true
// 下面三個(gè)方法用于計(jì)算使用當(dāng)前的EventLoop的runBlocking方法和Unconfined調(diào)度器的數(shù)量
// useCount是一個(gè)64位的數(shù),
// 它的高32位用于記錄Unconfined調(diào)度器的數(shù)量,低32位用于記錄runBlocking方法的數(shù)量
private fun delta(unconfined: Boolean) =
if (unconfined) (1L shl 32) else 1L
fun incrementUseCount(unconfined: Boolean = false) {
useCount += delta(unconfined)
// runBlocking中使用,shared為true
if (!unconfined) shared = true
}
fun decrementUseCount(unconfined: Boolean = false) {
useCount -= delta(unconfined)
// 如果EventLoop還在被使用
if (useCount > 0) return
assert { useCount == 0L }
// 如果EventLoop不被使用了,并且在EventLoop中使用過(guò)
if (shared) {
// 關(guān)閉相關(guān)資源,并在ThreadLocal中移除
shutdown()
}
}
protected open fun shutdown() {}
}協(xié)程中提供了EventLoopImplBase類(lèi),間接繼承自EventLoop,實(shí)現(xiàn)了Delay接口,用來(lái)延時(shí)執(zhí)行任務(wù)。同時(shí),協(xié)程中還提供單例對(duì)象ThreadLocalEventLoop用于EventLoop在ThreadLocal中的存儲(chǔ)。
到此這篇關(guān)于Kotlin圖文并茂講解續(xù)體與續(xù)體攔截器和調(diào)度器的文章就介紹到這了,更多相關(guān)Kotlin續(xù)體內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Android實(shí)現(xiàn)啟動(dòng)引導(dǎo)圖
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)啟動(dòng)引導(dǎo)圖,文中示例代碼介紹的非常詳細(xì),具有為大家詳細(xì)一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-06-06
Android studio保存logcat日志到本地的操作
這篇文章主要介紹了Android studio保存logcat日志到本地的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-04-04
Android應(yīng)用閃屏頁(yè)延遲跳轉(zhuǎn)的三種寫(xiě)法
這篇文章主要介紹了 Android應(yīng)用閃屏頁(yè)延遲跳轉(zhuǎn)的三種寫(xiě)法,需要的朋友可以參考下2017-03-03
Andoroid實(shí)現(xiàn)底部圖片選擇Dialog效果
這篇文章主要介紹了Andoroid實(shí)現(xiàn)底部圖片選擇Dialog效果,需要的朋友可以參考下2017-10-10
Android 創(chuàng)建依賴(lài)庫(kù)的方法(保姆級(jí)教程)
這篇文章主要介紹了Android 創(chuàng)建依賴(lài)庫(kù)的方法(保姆級(jí)教程),本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-01-01
Flutter進(jìn)階之實(shí)現(xiàn)動(dòng)畫(huà)效果(三)
這篇文章主要為大家詳細(xì)介紹了Flutter進(jìn)階之實(shí)現(xiàn)動(dòng)畫(huà)效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-08-08

