<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
EmptyCoroutineContext代表空上下文,由於自身為空,因此get方法的返回值是空的,fold方法直接返回傳入的初始值,plus方法也是直接返回傳入的context,minusKey方法返回自身,程式碼如下:
public object EmptyCoroutineContext : CoroutineContext, Serializable { private const val serialVersionUID: Long = 0 private fun readResolve(): Any = EmptyCoroutineContext public override fun <E : Element> get(key: Key<E>): E? = null public override fun <R> fold(initial: R, operation: (R, Element) -> R): R = initial public override fun plus(context: CoroutineContext): CoroutineContext = context public override fun minusKey(key: Key<*>): CoroutineContext = this public override fun hashCode(): Int = 0 public override fun toString(): String = "EmptyCoroutineContext" }
CombinedContext是組合上下文,是儲存Element的重要的資料結構。內部儲存的組織結構如下圖所示:
可以看出CombinedContext是一種左偏(從左向右計算)的列表,這麼設計的目的是為了讓CoroutineContext中的plus方法工作起來更加自然。
由於採用這種資料結構,CombinedContext類中的很多方法都是通過迴圈實現的,程式碼如下:
internal class CombinedContext( // 資料結構左邊可能為一個Element物件或者還是一個CombinedContext物件 private val left: CoroutineContext, // 資料結構右邊只能為一個Element物件 private val element: Element ) : CoroutineContext, Serializable { override fun <E : Element> get(key: Key<E>): E? { var cur = this while (true) { // 進行get操作,如果當前CombinedContext物件中存在,則返回 cur.element[key]?.let { return it } // 獲取左邊的上下文物件 val next = cur.left // 如果是CombinedContext物件 if (next is CombinedContext) { // 賦值,繼續迴圈 cur = next } else { // 如果不是CombinedContext物件 // 進行get操作,返回 return next[key] } } } // 資料結構左右分開操作,從左到右進行fold運算 public override fun <R> fold(initial: R, operation: (R, Element) -> R): R = operation(left.fold(initial, operation), element) public override fun minusKey(key: Key<*>): CoroutineContext { // 如果右邊是指定的Element物件,則返回左邊 element[key]?.let { return left } // 呼叫左邊的minusKey方法 val newLeft = left.minusKey(key) return when { // 這種情況,說明左邊部分已經是去掉指定的Element物件的,右邊也是如此,因此返回當前物件,不需要在進行包裹 newLeft === left -> this // 這種情況,說明左邊部分包含指定的Element物件,因此返回只右邊 newLeft === EmptyCoroutineContext -> element // 這種情況,返回的左邊部分是新的,因此需要和右邊部分一起包裹後,再返回 else -> CombinedContext(newLeft, element) } } private fun size(): Int { var cur = this //左右各一個 var size = 2 while (true) { cur = cur.left as? CombinedContext ?: return size size++ } } // 通過get方法實現 private fun contains(element: Element): Boolean = get(element.key) == element private fun containsAll(context: CombinedContext): Boolean { var cur = context // 迴圈展開每一個CombinedContext物件,每個CombinedContext物件中的Element物件都要包含 while (true) { if (!contains(cur.element)) return false val next = cur.left if (next is CombinedContext) { cur = next } else { return contains(next as Element) } } } ... }
Key介面與Element介面定義在CoroutineContext介面中,程式碼如下:
public interface Key<E : Element> public interface Element : CoroutineContext { // 一個Key對應著一個Element物件 public val key: Key<*> // 相等則強制轉換並返回,否則則返回空 public override operator fun <E : Element> get(key: Key<E>): E? = @Suppress("UNCHECKED_CAST") if (this.key == key) this as E else null // 自身與初始值進行fold操作 public override fun <R> fold(initial: R, operation: (R, Element) -> R): R = operation(initial, this) // 如果要去除的是當前的Element物件,則返回空的上下文,否則返回自身 public override fun minusKey(key: Key<*>): CoroutineContext = if (this.key == key) EmptyCoroutineContext else this }
CoroutineContext介面定義了協程上下文的基本行為以及Key和Element介面。同時,過載了"+"操作,相關程式碼如下:
public interface CoroutineContext { public operator fun <E : Element> get(key: Key<E>): E? public fun <R> fold(initial: R, operation: (R, Element) -> R): R public operator fun plus(context: CoroutineContext): CoroutineContext = // 如果要與空上下文相加,則直接但會當前物件, if (context === EmptyCoroutineContext) this else // 當前Element作為初始值 context.fold(this) { acc, element -> // acc:已經加完的CoroutineContext物件 // element:當前要加的CoroutineContext物件 // 獲取從acc中去掉element後的上下文removed,這步是為了確保新增重複的Element時,移動到最右側 val removed = acc.minusKey(element.key) // 去除掉element後為空上下文(說明acc中只有一個Element物件),則返回element if (removed === EmptyCoroutineContext) element else { // ContinuationInterceptor代表攔截器,也是一個Element物件 // 下面的操作是為了把攔截器移動到上下文的最右端,為了方便快速獲取 // 從removed中獲取攔截器 val interceptor = removed[ContinuationInterceptor] // 若上下文中沒有攔截器,則進行累加(包裹成CombinedContext物件),返回 if (interceptor == null) CombinedContext(removed, element) else { // 若上下文中有攔截器 // 獲取上下文中移除到掉攔截器後的上下文left val left = removed.minusKey(ContinuationInterceptor) // 若移除到掉攔截器後的上下文為空上下文,說明上下文left中只有一個攔截器, // 則進行累加(包裹成CombinedContext物件),返回 if (left === EmptyCoroutineContext) CombinedContext(element, interceptor) else // 否則,現對當前要加的element和left進行累加,然後在和攔截器進行累加 CombinedContext(CombinedContext(left, element), interceptor) } } } public fun minusKey(key: Key<*>): CoroutineContext ... // (Key和Element介面) }
假設我們有一個上下文順序為A、B、C,現在要按順序加上D、C、A。
1)初始值A、B、C
2)加上D
3)加上C
4)加上A
在協程中有大量的場景需要獲取ContinuationInterceptor。根據之前分析的CombinedContext的minusKey方法,ContinuationInterceptor放在上下文的最右端,可以直接獲取,不需要經過多次的迴圈。
AbstractCoroutineContextElement實現了Element介面,將Key物件作為構造方法必要的引數。
public abstract class AbstractCoroutineContextElement(public override val key: Key<*>) : Element
AbstractCoroutineContextKey用於實現Element的多型。什麼是Element的多型呢?假設類A實現了Element介面,Key為A。類B繼承自類A,Key為B。這時將類B的物件新增到上下文中,通過指定不同的Key(A或B),可以得到不同型別物件。具體程式碼如下:
// baseKey為衍生類的基礎類別的Key // safeCast用於對基礎類別進行轉換 // B為基礎類別,E為衍生類 public abstract class AbstractCoroutineContextKey<B : Element, E : B>( baseKey: Key<B>, private val safeCast: (element: Element) -> E? ) : Key<E> { // 頂置Key,如果baseKey是AbstractCoroutineContextKey,則獲取baseKey的頂置Key private val topmostKey: Key<*> = if (baseKey is AbstractCoroutineContextKey<*, *>) baseKey.topmostKey else baseKey // 用於型別轉換 internal fun tryCast(element: Element): E? = safeCast(element) // 用於判斷當前key是否是指定key的子key // 邏輯為與當前key相同,或者與當前key的頂置key相同 internal fun isSubKey(key: Key<*>): Boolean = key === this || topmostKey === key }
getPolymorphicElement方法與minusPolymorphicKey方法
如果衍生類使用了AbstractCoroutineContextKey,那麼基礎類別在實現Element介面中的get方法時,就需要通過getPolymorphicElement方法,實現minusKey方法時,就需要通過minusPolymorphicKey方法,程式碼如下:
public fun <E : Element> Element.getPolymorphicElement(key: Key<E>): E? { // 如果key是AbstractCoroutineContextKey if (key is AbstractCoroutineContextKey<*, *>) { // 如果key是當前key的子key,則基礎類別強制轉換成衍生類,並返回 @Suppress("UNCHECKED_CAST") return if (key.isSubKey(this.key)) key.tryCast(this) as? E else null } // 如果key不是AbstractCoroutineContextKey // 如果key相等,則強制轉換,並返回 @Suppress("UNCHECKED_CAST") return if (this.key === key) this as E else null }
public fun Element.minusPolymorphicKey(key: Key<*>): CoroutineContext { // 如果key是AbstractCoroutineContextKey if (key is AbstractCoroutineContextKey<*, *>) { // 如果key是當前key的子key,基礎類別強制轉換後不為空,說明當前Element需要去掉,因此返回空上下文,否則返回自身 return if (key.isSubKey(this.key) && key.tryCast(this) != null) EmptyCoroutineContext else this } // 如果key不是AbstractCoroutineContextKey // 如果key相等,說明當前Element需要去掉,因此返回空上下文,否則返回自身 return if (this.key === key) EmptyCoroutineContext else this }
到此這篇關於Kotlin協程上下文與上下文元素深入理解的文章就介紹到這了,更多相關Kotlin上下文內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45