android 協程,關于android:Kotlin協程實現原理SuspendCoroutineContext

明天咱們來聊聊Kotlin的協程Coroutine。

如果你還沒有接觸過協程,舉薦你先瀏覽這篇入門級文章What? 你還不曉得Kotlin Coroutine?

如果你曾經接觸過協程,置信你都有過以下幾個疑難:

協程到底是個什么貨色?

協程的suspend有什么作用,工作原理是怎么的?

協程中的一些要害名稱(例如:Job、Coroutine、Dispatcher、CoroutineContext與CoroutineScope)它們之間到底是怎么樣的關系?

協程的所謂非阻塞式掛起與復原又是什么?

協程的外部實現原理是怎么樣的?

接下來的一些文章試著來剖析一下這些疑難,也歡送大家一起退出來探討。

協程是什么

這個疑難很簡略,只有你不是野路子接觸協程的,都應該可能曉得。因為官網文檔中曾經明確給出了定義。

上面來看下官網的原話(也是這篇文章最具備底氣的一段話)。

協程是一種并發設計模式,您能夠在 Android 平臺上應用它來簡化異步執行的代碼。

敲黑板劃重點:協程是一種并發的設計模式。

所以并不是一些人所說的什么線程的另一種體現。盡管協程的外部也應用到了線程。但它更大的作用是它的設計思維。將咱們傳統的Callback回調形式進行打消。將異步編程趨近于同步對齊。

解釋了這么多,最初咱們還是間接點,來看下它的長處

輕量:您能夠在單個線程上運行多個協程,因為協程反對掛起,不會使正在運行協程的線程阻塞。掛起比阻塞節儉內存,且反對多個并行操作。

內存泄露更少:應用結構化并發機制在一個作用域內執行多個操作。

內置勾銷反對:勾銷性能會主動通過正在運行的協程層次結構流傳。

Jetpack集成:許多 Jetpack 庫都蘊含提供全面協程反對的擴大。某些庫還提供本人的協程作用域,可供您用于結構化并發。

suspend

suspend是協程的關鍵字,每一個被suspend潤飾的辦法都必須在另一個suspend函數或者Coroutine協程程序中進行調用。

第一次看到這個定義不曉得你們是否有疑難,反正小憩我是很納悶,為什么suspend潤飾的辦法須要有這個限度呢?不加為什么就不能夠,它的作用到底是什么?

當然,如果你有關注我之前的文章,應該就會有所理解,因為在重溫Retrofit源碼,笑看協程實現這篇文章中我曾經有簡略的提及。

這里波及到一種機制俗稱CPS(Continuation-Passing-Style)。每一個suspend潤飾的辦法或者lambda表達式都會在代碼調用的時候為其額定增加Continuation類型的參數。

@GET("/v2/news")

suspend fun newsGet(@QueryMap params: Map): NewsResponse

下面這段代碼通過CPS轉換之后真正的面目是這樣的

@GET("/v2/news")

fun newsGet(@QueryMap params: Map, c: Continuation): Any?

通過轉換之后,原有的返回類型NewsResponse被增加到新增的Continutation參數中,同時返回了Any?類型。這里可能會有所疑難?返回類型都變了,后果不就出錯了嗎?

其實不是,Any?在Kotlin中比擬非凡,它能夠代表任意類型。

當suspend函數被協程掛起時,它會返回一個非凡的標識COROUTINE_SUSPENDED,而它實質就是一個Any;當協程不掛起進行執行時,它將返回執行的后果或者引發的異樣。這樣為了讓這兩種狀況的返回都反對,所以應用了Kotlin獨有的Any?類型。

返回值搞明確了,當初來說說這個Continutation參數。

首先來看下Continutation的源碼

public interface Continuation {

/**

* The context of the coroutine that corresponds to this continuation.

*/

public val context: CoroutineContext

/**

* Resumes the execution of the corresponding coroutine passing a successful or failed [result] as the

* return value of the last suspension point.

*/

public fun resumeWith(result: Result)

}

context是協程的上下文,它更多時候是CombinedContext類型,相似于協程的匯合,這個后續會詳情闡明。

resumeWith是用來喚醒掛起的協程。后面曾經說過協程在執行的過程中,為了避免阻塞應用了掛起的個性,一旦協程外部的邏輯執行結束之后,就是通過該辦法來喚起協程。讓它在之前掛起的地位繼續執行上來。

所以每一個被suspend潤飾的函數都會獲取下層的Continutation,并將其作為參數傳遞給本人。既然是從下層傳遞過去的,那么Continutation是由誰創立的呢?

其實也不難猜到,Continutation就是與協程創立的時候一起被創立的。

GlobalScope.launch {

}

launch的時候就曾經創立了Continutation對象,并且啟動了協程。所以在它外面進行掛起的協程傳遞的參數都是這個對象。

簡略的了解就是協程應用resumeWith替換傳統的callback,每一個協程程序的創立都會隨同Continutation的存在,同時協程創立的時候都會主動回調一次Continutation的resumeWith辦法,以便讓協程開始執行。

CoroutineContext

協程的上下文,它蘊含用戶定義的一些數據匯合,這些數據與協程密切相關。它相似于map匯合,能夠通過key來獲取不同類型的數據。同時CoroutineContext的靈活性很強,如果其須要扭轉只需應用以后的CoroutineContext來創立一個新的CoroutineContext即可。

來看下CoroutineContext的定義

public interface CoroutineContext {

/**

* Returns the element with the given [key] from this context or `null`.

*/

public operator fun get(key: Key): E?

/**

* Accumulates entries of this context starting with [initial] value and applying [operation]

* from left to right to current accumulator value and each element of this context.

*/

public fun fold(initial: R, operation: (R, Element) -> R): R

/**

* Returns a context containing elements from this context and elements from other [context].

* The elements from this context with the same key as in the other one are dropped.

*/

public operator fun plus(context: CoroutineContext): CoroutineContext = ...

/**

* Returns a context containing elements from this context, but without an element with

* the specified [key].

*/

public fun minusKey(key: Key): CoroutineContext

/**

* Key for the elements of [CoroutineContext]. [E] is a type of element with this key.

*/

public interface Key

/**

* An element of the [CoroutineContext]. An element of the coroutine context is a singleton context by itself.

*/

public interface Element : CoroutineContext {..}

}

每一個CoroutineContext都有它惟一的一個Key其中的類型是Element,咱們能夠通過對應的Key來獲取對應的具體對象。說的有點形象咱們間接通過例子來理解。

var context = Job() + Dispatchers.IO + CoroutineName("aa")

LogUtils.d("$context, ${context[CoroutineName]}")

context = context.minusKey(Job)

LogUtils.d("$context")

// 輸入

[JobImpl{Active}@158b42c, CoroutineName(aa), LimitingDispatcher@aeb0f27[dispatcher = DefaultDispatcher]], CoroutineName(aa)

[CoroutineName(aa), LimitingDispatcher@aeb0f27[dispatcher = DefaultDispatcher]]

Job、Dispatchers與CoroutineName都實現了Element接口。

如果須要聯合不同的CoroutineContext能夠間接通過+拼接,實質就是應用了plus辦法。

public operator fun plus(context: CoroutineContext): CoroutineContext =

if (context === EmptyCoroutineContext) this else // fast path -- avoid lambda creation

context.fold(this) { acc, element ->

val removed = acc.minusKey(element.key)

if (removed === EmptyCoroutineContext) element else {

// make sure interceptor is always last in the context (and thus is fast to get when present)

val interceptor = removed[ContinuationInterceptor]

if (interceptor == null) CombinedContext(removed, element) else {

val left = removed.minusKey(ContinuationInterceptor)

if (left === EmptyCoroutineContext) CombinedContext(element, interceptor) else

CombinedContext(CombinedContext(left, element), interceptor)

}

}

}

plus的實現邏輯是將兩個拼接的CoroutineContext封裝到CombinedContext中組成一個拼接鏈,同時每次都將ContinuationInterceptor增加到拼接鏈的最尾部.

那么CombinedContext又是什么呢?

internal class CombinedContext(

private val left: CoroutineContext,

private val element: Element

) : CoroutineContext, Serializable {

override fun get(key: Key): E? {

var cur = this

while (true) {

cur.element[key]?.let { return it }

val next = cur.left

if (next is CombinedContext) {

cur = next

} else {

return next[key]

}

}

}

...

}

留神看它的兩個參數,咱們間接拿下面的例子來剖析

Job() + Dispatchers.IO

(Job, Dispatchers.IO)

Job對應于left,Dispatchers.IO對應element。如果再拼接一層CoroutineName(aa)就是這樣的

((Job, Dispatchers.IO),CoroutineName)

性能相似與鏈表,但不同的是你可能拿到上一個與你相連的整體內容。與之對應的就是minusKey辦法,從匯合中移除對應Key的CoroutineContext實例。

有了這個根底,咱們再看它的get辦法就很清晰了。先從element中去取,沒有再從之前的left中取。

那么這個Key到底是什么呢?咱們來看下CoroutineName

public data class CoroutineName(

/**

* User-defined coroutine name.

*/

val name: String

) : AbstractCoroutineContextElement(CoroutineName) {

/**

* Key for [CoroutineName] instance in the coroutine context.

*/

public companion object Key : CoroutineContext.Key

/**

* Returns a string representation of the object.

*/

override fun toString(): String = "CoroutineName($name)"

}

很簡略它的Key就是CoroutineContext.Key,當然這樣還不夠,須要持續聯合對于的operator get辦法,所以咱們再來看下Element的get辦法

public override operator fun get(key: Key): E? =

@Suppress("UNCHECKED_CAST")

if (this.key == key) this as E else null

這里應用到了Kotlin的operator操作符重載的個性。那么上面的代碼就是等效的。

context.get(CoroutineName)

context[CoroutineName]

所以咱們就能夠間接通過相似于Map的形式來獲取整個協程中CoroutineContext匯合中對應Key的CoroutineContext實例。

本篇文章次要介紹了suspend的工作原理與CoroutineContext的內部結構。心愿對學習協程的搭檔們可能有所幫忙,敬請期待后續的協程剖析。

我的項目

android_startup: 提供一種在利用啟動時可能更加簡略、高效的形式來初始化組件,優化啟動速度。不僅反對Jetpack App Startup的全副性能,還提供額定的同步與異步期待、線程管制與多過程反對等性能。

AwesomeGithub: 基于Github客戶端,純練習我的項目,反對組件化開發,反對賬戶明碼與認證登陸。應用Kotlin語言進行開發,我的項目架構是基于Jetpack&DataBinding的MVVM;我的項目中應用了Arouter、Retrofit、Coroutine、Glide、Dagger與Hilt等風行開源技術。

flutter_github: 基于Flutter的跨平臺版本Github客戶端,與AwesomeGithub絕對應。

android-api-analysis: 聯合具體的Demo來全面解析Android相干的知識點, 幫忙讀者可能更快的把握與了解所論述的要點。

daily_algorithm: 每日一算法,由淺入深,歡送退出一起共勉。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/370073.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/370073.shtml
英文地址,請注明出處:http://en.pswp.cn/news/370073.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

清空easyui checkbox選中項

$(#dg).datagrid(unselectAll);轉載于:https://www.cnblogs.com/douhuan/p/7116744.html

python 編輯excel需要什么包_Python 中操作EXCEL表格的包

今天,馬云爸爸又來貢獻金句了,比王健林公公一億一個小目標還高,“一個月掙一二十個億很難受!!!”,作為在傳統企業主要為電商部門提供數據分析的數據分析師,體驗太深刻了。雙11前后&a…

用Java處理大文件

最近,我不得不處理一組包含逐筆歷史匯率市場數據的文件,并很快意識到使用傳統的InputStream都無法將它們讀取到內存中,因為每個文件的大小都超過4 GB。 Emacs甚至無法打開它們。 在這種特殊情況下,我可以編寫一個簡單的bash腳本&…

java IO(一):File類

1.File類簡介 File類位于java.io包中。它面向文件層次級別操作、查看文件,而字節流、字符流操作數據時顯然比之更底層。 學習File類包括以下幾個重點:文件路徑、文件分隔符、創建文件(目錄)、刪除文件(目錄)、查看文件內容(輸出目錄內文件)、判斷文件(是…

android listview 開發,android開發之ListView實現

今天又初步學習了一下ListView控件,看看效果如下:LisViewActivity.java源碼:package com.jinhoward.UI_listview;import java.util.ArrayList;import java.util.HashMap;import java.util.List;import java.util.Map;import android.os.Bundl…

input ios問題 小程序_微信小程序開發常見問題匯總

原標題:微信小程序開發常見問題匯總1、域名必須是https非https的域名不被微信小程序允許。2、input組件placeholder字體顏色卸載placeholder-class里面的color并不生效,需要寫在placeholder-style里面就可以了。3、wx.navigateTo無法跳轉到帶tabbar的頁面…

https://github.com/

https://github.com/ qq郵箱 轉載于:https://www.cnblogs.com/chang1/p/7133251.html

Less 的用法

1. node.js node.js是一個前端的框架 自帶一個包管理工具npm node.js 的安裝 官網:http://nodejs.cn/ 在命令行檢驗是否安裝成功 切換到項目目錄,初始化了一個package.json文件 安裝與卸載jQuery包(例子) 安裝 卸載 安裝淘寶…

淺談springboot整合ganymed-ssh2遠程訪問linux

環境介紹 技術棧 springbootmybatis-plusmysqlganymed-ssh2 軟件 版本 mysql 8 IDEA IntelliJ IDEA 2022.2.1 JDK 1.8 Spring Boot 2.7.13 mybatis-plus 3.5.3.2 SSH(遠程連接工具)連接原理:ssh服務是一個守護進程(demon),系統后臺監聽客戶…

優化Neo4j Cypher查詢

上周,我花了很多時間嘗試使用實時系統中的數據來優化大約20個執行失敗的Cypher查詢(36866ms至155575ms)。 經過一番嘗試和錯誤,以及來自Michael的大量投入,我能夠大致確定對查詢進行哪些操作才能使它們性能更好-最后&a…

python 多文件知識

對于一個大型的項目,會存在很多個py文件,本文記錄與多文件有關的內容。 1. python 如何在一個.py文件中調用另一個.py文件的類 如果是在同一個 module中(也就是同一個py 文件里),直接用就可以如果在不同的module里,例如a.py里有 class A:b.py 里有 class…

android pick file,LFilePicker---文件選擇利器,各種樣式有它就夠了

LFilePicker在 Android 開發中如果需要選擇某個文件,可以直接調取系統的文件管理器進行選擇,但是無法保證各個廠商的手機界面一致,而且解析Uri 還比較繁瑣,如果還需要多選呢?需要文件類型過濾呢?老板說界面…

老筆記整理二:網頁小問題匯總

最近有一些小問題。想在這里寫出來。一是方便大家排錯,再是自己也整理一下。 1。很傻的小問題。。。參數提交方式有一個應該是form而不是from。(英語老師,我對不起你。。。) 2。用超鏈接傳參數,在?后面不能…

在JVM之下–類加載器

在許多開發人員中,類加載器是Java語言的底層,并且經常被忽略。 在ZeroTurnaround上 ,我們的開發人員必須生活,呼吸,飲食,喝酒,并且幾乎與類加載器保持親密關系,才能生產JRebel技術&a…

matplotlib繪制餅狀圖

源自http://blog.csdn.net/skyli114/article/details/77508430?ticketST-41707-PzNbUDGt6R5KYl3TkWDg-passport.csdn.net pyplot使用plt.pie()來繪制餅圖 1 import matplotlib.pyplot as plt 2 labels frogs, hogs, dogs, logs 3 sizes 15, 20, 45, 10 # [15,20,45,10…

自適應寬度元素單行文本省略用法探究

單行文本省略是現代網頁設計中非常常用的技術,幾乎每個站點都會用到。單行文本省略適用于顯示摘要信息的場景,如列表標題、文章摘要等。在響應式開發中,自適應寬度元素單行文本省略容易失效不起作用,對網頁開發這造成困擾。因此&a…

P3390 【模板】矩陣快速冪

題目背景 矩陣快速冪 題目描述 給定n*n的矩陣A,求A^k 輸入輸出格式 輸入格式: 第一行,n,k 第2至n1行,每行n個數,第i1行第j個數表示矩陣第i行第j列的元素 輸出格式: 輸出A^k 共n行,每行n個數&…

c#精彩編程200例百度云_永安市教育局被授予“人工智能編程教育試驗區”

11月28日,“第二屆人工智能與機器人教育大會青少年人工智能與編程教育主題論壇”在廈門召開。永安市教育局被中國教育發展戰略學會人工智能與機器人教育專委會授予“人工智能編程教育試驗區”牌匾,巴溪灣小學、西門小學、三中、一中附屬學校、實驗小學等…

python中+=和=+的區別

本文原創,版權屬作者個人所有,如需轉載請聯系作者本人。Q&微:155122733 -------------------------------------------------------------------------------------------------------- a1 代表在原值上更改 aa1相當于先定義一個變量&…

Spring Data JPA和分頁

讓我們從支持分頁的經典JPA方法開始。 考慮一個簡單的域類–一個具有名字,姓氏的“成員”。 為了支持在成員列表上進行分頁,JPA方法是支持一種查找器,該查找器將獲取第一個結果(firstResult)的偏移量和要檢索的結果&am…