Kotlin協(xié)程Context應(yīng)用使用示例詳解
1.Context的應(yīng)用
Context在啟動(dòng)協(xié)程模式中就已經(jīng)遇到過(guò)叫CoroutineContext,它的意思就是協(xié)程上下文,線程的切換離不開(kāi)它。
在啟動(dòng)協(xié)程模式中也說(shuō)明過(guò)為什么不用傳遞Context,因?yàn)樗幸粋€(gè)默認(rèn)值EmptyCoroutineContext,需要注意的是這個(gè)Context是不可以切換線程的因?yàn)樗且粋€(gè)空的上下文對(duì)象,如果有這個(gè)需求就需要傳入具體的Context,例如Dispatchers.IO。
//launch
public fun CoroutineScope.launch(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> Unit
): Job {
val newContext = newCoroutineContext(context)
val coroutine = if (start.isLazy)
LazyStandaloneCoroutine(newContext, block) else
StandaloneCoroutine(newContext, active = true)
coroutine.start(start, coroutine, block)
return coroutine
}
//runBlocking
public actual fun <T> runBlocking(context: CoroutineContext, block: suspend CoroutineScope.() -> T): T {
}
//async
public fun <T> CoroutineScope.async(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> T
): Deferred<T> {
val newContext = newCoroutineContext(context)
val coroutine = if (start.isLazy)
LazyDeferredCoroutine(newContext, block) else
DeferredCoroutine<T>(newContext, active = true)
coroutine.start(start, coroutine, block)
return coroutine
}
當(dāng)傳入Dispatchers.IO時(shí)執(zhí)行的線程有什么變化呢?
fun main() = runBlocking {
val user = contextTest()
logX(user)
}
suspend fun contextTest(): String {
logX("Start Context")
withContext(Dispatchers.IO) {
logX("Loading Context.")
delay(1000L)
}
logX("After Context.")
return "End Context"
}
fun logX(any: Any?) {
println(
"""
================================
$any
Thread:${Thread.currentThread().name}
================================
""".trimIndent()
)
}
//輸出結(jié)果:
//================================
//Start Context
//Thread:main @coroutine#1
//================================
//================================
//Loading Context.
//Thread:DefaultDispatcher-worker-1 @coroutine#1
//================================
//================================
//After Context.
//Thread:main @coroutine#1
//================================
//================================
//End Context
//Thread:main @coroutine#1
//================================
從輸出結(jié)果可以得出一個(gè)結(jié)論:默認(rèn)是運(yùn)行在main線程中當(dāng)傳入Dispatchers.IO之后就會(huì)進(jìn)入到IO線程執(zhí)行,然后在IO線程執(zhí)行完畢后又回到了main線程,那么除了這兩個(gè)線程之外是否還有其他線程呢?答案是有,除了這兩個(gè)之外還有2個(gè):
public actual object Dispatchers {
/**
* 用于CPU密集型任務(wù)的線程池,一般來(lái)說(shuō)它內(nèi)部的線程個(gè)數(shù)是與機(jī)器 CPU 核心數(shù)量保持一致的
* 不過(guò)它有一個(gè)最小限制2,
*/
public actual val Default: CoroutineDispatcher = DefaultScheduler
/**
* 主線程,在Android中才可以使用,主要用于UI的繪制,在普通JVM上無(wú)法使用
*/
public actual val Main: MainCoroutineDispatcher get() = MainDispatcherLoader.dispatcher
/**
* 不局限于任何特定線程,會(huì)根據(jù)運(yùn)行時(shí)的上下文環(huán)境決定
*/
public actual val Unconfined: CoroutineDispatcher = kotlinx.coroutines.Unconfined
/**
* 用于執(zhí)行IO密集型任務(wù)的線程池,它的數(shù)量會(huì)多一些,默認(rèn)最大線程數(shù)量為64個(gè)
* 具體的線程數(shù)量可以通過(guò)kotlinx.coroutines.io.parallelism配置
* 它會(huì)和Default共享線程,當(dāng)Default還有其他空閑線程時(shí)是可以被IO線程池復(fù)用。
*/
public val IO: CoroutineDispatcher = DefaultIoScheduler
}
除了上述幾個(gè)Dispatcher之外還可以自定義Dispatcher
fun main() = runBlocking {
val user = contextTest()
logX(user)
}
suspend fun contextTest(): String {
logX("Start Context")
//使用自定義的dispatcher
// ↓
withContext(myDispatcher) {
logX("Loading Context.")
delay(100L)
}
logX("After Context.")
return "End Context"
}
val myDispatcher = Executors.newSingleThreadExecutor {
Thread(it, "myDispatcher").apply { isDaemon = true }
}.asCoroutineDispatcher()
//輸出結(jié)果
//================================
//Start Context
//Thread:main @coroutine#1
//================================
//================================
//Loading Context.
//Thread:myDispatcher @coroutine#1
//================================
//================================
//After Context.
//Thread:main @coroutine#1
//================================
//================================
//End Context
//Thread:main @coroutine#1
//================================
通過(guò) asCoroutineDispatcher() 這個(gè)擴(kuò)展函數(shù),創(chuàng)建了一個(gè) Dispatcher。從這里也能看到,Dispatcher 的本質(zhì)仍然還是線程。那么可以得出一個(gè)結(jié)論:協(xié)程是運(yùn)行在線程之上的。
前面還有一個(gè)線程Unconfined,它是一個(gè)特殊的線程,沒(méi)有指定可運(yùn)行在哪里,但是這個(gè)使用時(shí)需要謹(jǐn)慎甚至最好不用,通過(guò)下面的的代碼對(duì)比一下:
//不設(shè)置執(zhí)行線程
fun main() = runBlocking {
logX("Start launch.")
launch {
logX("Start Delay launch.")
delay(1000L)
logX("End Delay launch.")
}
logX("End launch")
}
//輸出結(jié)果
//================================
//Start launch.
//Thread:main @coroutine#1
//================================
//================================
//End launch
//Thread:main @coroutine#1
//================================
//================================
//Start Delay launch.
//Thread:main @coroutine#2
//================================
//================================
//End Delay launch.
//Thread:main @coroutine#2
//================================
//設(shè)置執(zhí)行線程
fun main() = runBlocking {
logX("Start launch.")
// 變化在這里
// ↓
launch(Dispatchers.Unconfined) {
logX("Start Delay launch.")
delay(1000L)
logX("End Delay launch.")
}
logX("End launch")
}
//輸出結(jié)果
//================================
//Start launch.
//Thread:main @coroutine#1
//================================
//================================
//Start Delay launch.
//Thread:main @coroutine#2
//================================
//================================
//End launch
//Thread:main @coroutine#1
//================================
//================================
//End Delay launch.
//Thread:kotlinx.coroutines.DefaultExecutor @coroutine#2
//================================
經(jīng)過(guò)對(duì)比可以發(fā)現(xiàn)加入Dispatchers.Unconfined會(huì)導(dǎo)致代碼的運(yùn)行順序被修改,這種錯(cuò)誤的產(chǎn)生一定會(huì)對(duì)項(xiàng)目調(diào)試造成非常大的影響,而且Dispatchers.Unconfined的定義初衷也不是為了修改代碼的執(zhí)行順序。
2.萬(wàn)物皆有 Context
在Kotlin協(xié)程中,但凡是重要的概念都直接或間接的與CoroutineContext有關(guān)系,例如Job、Dispatcher、CoroutineExceptionHandler、CoroutineScope等
1.CoroutineScope
public fun CoroutineScope.launch(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> Unit
): Job {
val newContext = newCoroutineContext(context)
val coroutine = if (start.isLazy)
LazyStandaloneCoroutine(newContext, block) else
StandaloneCoroutine(newContext, active = true)
coroutine.start(start, coroutine, block)
return coroutine
}
/**
* CoroutineScope的作用域就是把CoroutineContext做了一層封裝,核心實(shí)現(xiàn)均來(lái)自于CoroutineContext
*/
public interface CoroutineScope {
/**
* 此作用域的上下文。上下文被作用域封裝,并用于實(shí)現(xiàn)作為作用域擴(kuò)展的協(xié)程構(gòu)建器
*/
public val coroutineContext: CoroutineContext
}
CoroutineScope的源碼注釋寫的很清楚,核心實(shí)現(xiàn)在于CoroutineContext,CoroutineScope只是做了封裝而已,然后就可以批量的控制協(xié)程了,例如下面的代碼實(shí)現(xiàn):
fun main() = runBlocking {
val scope = CoroutineScope(Job())
scope.launch {
logX("launch 1")
}
scope.launch {
logX("launch 2")
}
scope.launch {
logX("launch 3")
}
scope.launch {
logX("launch 4")
}
delay(500L)
scope.cancel()
delay(1000L)
}
2.Job
//Job#Job
public interface Job : CoroutineContext.Element {
}
//CoroutineContext#Element
public interface CoroutineContext {
/**
* 從該上下文返回具有給定鍵的元素,或返回null
*/
public operator fun <E : Element> get(key: Key<E>): E?
/**
* 從初始值開(kāi)始累積此上下文的條目,并從左到右對(duì)當(dāng)前累加器值和此上下文的每個(gè)元素應(yīng)用操作。
*/
public fun <R> fold(initial: R, operation: (R, Element) -> R): R
/**
* 返回包含該上下文和其他上下文元素的上下文。
* 刪除這個(gè)上下文中與另一個(gè)上下文中具有相同鍵的元素。
*/
public operator fun plus(context: CoroutineContext): CoroutineContext {}
/**
* 返回包含此上下文中的元素的上下文,但不包含具有指定鍵的元素。
*/
public fun minusKey(key: Key<*>): CoroutineContext
/**
* CoroutineContext元素的鍵
*/
public interface Key<E : Element>
/**
* CoroutineContext的一個(gè)元素。協(xié)程上下文的一個(gè)元素本身就是一個(gè)單例上下文。
*/
public interface Element : CoroutineContext {
}
}
Job實(shí)現(xiàn)了CoroutineContext.Element,CoroutineContext.Element又實(shí)現(xiàn)了CoroutineContext那么就可以認(rèn)為Job間接實(shí)現(xiàn)了CoroutineContext,所以可以認(rèn)定Job就是一個(gè)CoroutineContext。
所以在定義Job時(shí)下面兩種定義方式都可以:
val job: CoroutineContext = Job() val job: Job = Job()
3.Dispatcher
public actual object Dispatchers {
public actual val Default: CoroutineDispatcher = DefaultScheduler
public actual val Main: MainCoroutineDispatcher get() = MainDispatcherLoader.dispatcher
public actual val Unconfined: CoroutineDispatcher = kotlinx.coroutines.Unconfined
public val IO: CoroutineDispatcher = DefaultIoScheduler
public fun shutdown() { }
}
public abstract class CoroutineDispatcher :
AbstractCoroutineContextElement(ContinuationInterceptor), ContinuationInterceptor {}
public interface ContinuationInterceptor : CoroutineContext.Element {
Dispatcher中的每一個(gè)線程繼承自CoroutineDispatcher,CoroutineDispatcher實(shí)現(xiàn)了ContinuationInterceptor接口,ContinuationInterceptor又實(shí)現(xiàn)了CoroutineContext接口,由此就可以知道Dispatcher和CoroutineContext是如何產(chǎn)生關(guān)聯(lián)的了,或者說(shuō)Dispatcher就是CortinueContext。
4.CoroutineExceptionHandler
/**
* 協(xié)程上下文中一個(gè)可選的元素,用于處理未捕獲的異常
*/
public interface CoroutineExceptionHandler : CoroutineContext.Element {
/**
*
*/
public companion object Key : CoroutineContext.Key<CoroutineExceptionHandler>
/**
* 處理給定上下文中未捕獲的異常。如果協(xié)程有未捕獲的異常,則調(diào)用它。
*/
public fun handleException(context: CoroutineContext, exception: Throwable)
}
CoroutineExceptionHandler主要用來(lái)處理協(xié)程中未捕獲的異常,未捕獲的異常只能來(lái)自根協(xié)程,子協(xié)程未捕獲的異常會(huì)委托給它們的父協(xié)程,父協(xié)程也委托給父協(xié)程,以此類推,直到根協(xié)程。所以安裝在它們上下文中的CoroutineExceptionHandler永遠(yuǎn)不會(huì)被使用。


以上就是Kotlin協(xié)程Context應(yīng)用使用示例詳解的詳細(xì)內(nèi)容,更多關(guān)于Kotlin協(xié)程Context應(yīng)用的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Android開(kāi)發(fā)之Android studio的安裝與使用
本文是此系列文章的第一篇,主要給大家講述的是Android studio的安裝與使用,十分的詳細(xì),有需要的小伙伴可以參考下2016-02-02
新版Android studio導(dǎo)入微信支付和支付寶官方Demo問(wèn)題解決大全
這篇文章主要為大家詳細(xì)介紹了新版Android studio導(dǎo)入微信支付和支付寶官方Demo問(wèn)題的解決大全,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-07-07
Android 程序執(zhí)行Linux命令的解決方法及注意事項(xiàng)
這篇文章主要介紹了Android 程序執(zhí)行LINUX命令的解決方法及注意事項(xiàng),本文通過(guò)問(wèn)題描述最終到解決方法,給大家介紹的非常詳細(xì),需要的朋友可以參考下2017-12-12
android studio與手機(jī)連接調(diào)試步驟詳解
這篇文章主要為大家詳細(xì)介紹了android studio與手機(jī)連接調(diào)試步驟,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-07-07
android仿微信通訊錄搜索示例(匹配拼音,字母,索引位置)
本篇文章主要介紹了android仿微信通訊錄搜索示例(匹配拼音,字母,索引位置),具有一定的參考價(jià)值,有興趣的可以了解一下2017-09-09
Android調(diào)用系統(tǒng)照相機(jī)拍照與攝像的方法
這篇文章主要為大家詳細(xì)介紹了Android如何調(diào)用系統(tǒng)現(xiàn)有的照相機(jī)拍照與攝像,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-04-04
Android?Flutter實(shí)現(xiàn)任意拖動(dòng)的控件
使用flutter開(kāi)發(fā)是需要控件能拖動(dòng),比如畫板中的元素,或者工具條等,所以本文為大家準(zhǔn)備了Flutter實(shí)現(xiàn)任意拖動(dòng)控件的示例代碼,希望對(duì)大家有所幫助2023-07-07
Android 判斷ip地址合法實(shí)現(xiàn)代碼
這篇文章主要介紹了Android 判斷ip地址合法實(shí)現(xiàn)代碼的相關(guān)資料,需要的朋友可以參考下2017-06-06

