原理篇(一)關鍵類的分析
上面簡單的介紹了一些用法,但是具體的原理和特點,好像還不是很清楚,那麼下面就來介紹一下,一些關鍵的類,流程和原理。
介紹的相關的原理基於這行代碼:
fun coroTest() {
GlobalScope.launch {
delay(1000L)//Delays coroutine for a given time without blocking a thread and resumes it after a specified time
Log.i(CO_TAG, "launch ")
}
Log.i(CO_TAG, "----")
}
然後貼上 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
}
CoroutineScope
源碼分析
首先,launch() 是 CoroutineScope 的一個擴展函數,
CoroutineScope 簡單來說,就是協程的作用範圍,每一個 Coroutine Builder,例如 CoroutineScope.launch,都是 CoroutineScope 的擴展,並且繼承了其 coroutineContext .
Corotine 提供了全局的 CoroutineScope 也就是 GlobalScope,簡單看下其代碼:
//CoroutineScope.kt 中
public object GlobalScope : CoroutineScope {
/**
* Returns [EmptyCoroutineContext].
*/
override val coroutineContext: CoroutineContext
get() = EmptyCoroutineContext
}
//EmptyCorotineContext in CorotineContextImpl.kt 文件中
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"
}
簡單來說,GlobalScope 沒有綁定任何 job,它用於構建最頂級的 coroutines,這些協程的生命週期跟隨這個 Application,並且在 Application 生命週期結束之前,不會被 cancel。
關鍵函數分析
CoroutinScope 主要包含了以下擴展函數:
actor | fun <E> CoroutineScope.actor( context: CoroutineContext = EmptyCoroutineContext, capacity: Int = 0, start: CoroutineStart = CoroutineStart.DEFAULT, onCompletion: CompletionHandler? = null, block: suspend ActorScope<E>.() -> Unit): SendChannel<E> Launches new coroutine that is receiving messages from its mailbox channel and returns a reference to its mailbox channel as a SendChannel. The resulting object can be used to send messages to this coroutine. |
---|---|
async | fun <T> CoroutineScope.async( context: CoroutineContext = EmptyCoroutineContext, start: CoroutineStart = CoroutineStart.DEFAULT, block: suspend CoroutineScope.() -> T): Deferred<T> Creates new coroutine and returns its future result as an implementation of Deferred. The running coroutine is cancelled when the resulting deferred is cancelled. |
broadcast | fun <E> CoroutineScope.broadcast( context: CoroutineContext = EmptyCoroutineContext, capacity: Int = 1, start: CoroutineStart = CoroutineStart.LAZY, onCompletion: CompletionHandler? = null, block: suspend ProducerScope<E>.() -> Unit): BroadcastChannel<E> Launches new coroutine to produce a stream of values by sending them to a broadcast channel and returns a reference to the coroutine as a BroadcastChannel. The resulting object can be used to subscribe to elements produced by this coroutine. |
cancel | fun CoroutineScope.cancel(): Unit Cancels this scope, including its job and all its children. Throws IllegalStateExceptionif the scope does not have a job in it. |
launch | fun CoroutineScope.launch( context: CoroutineContext = EmptyCoroutineContext, start: CoroutineStart = CoroutineStart.DEFAULT, block: suspend CoroutineScope.() -> Unit): Job Launches new coroutine without blocking current thread and returns a reference to the coroutine as a Job. The coroutine is cancelled when the resulting job is cancelled. |
newCoroutineContext | fun CoroutineScope.newCoroutineContext( context: CoroutineContext): CoroutineContext Creates context for the new coroutine. It installs Dispatchers.Default when no other dispatcher nor ContinuationInterceptor is specified, and adds optional support for debugging facilities (when turned on). |
plus | operator fun CoroutineScope.plus( context: CoroutineContext): CoroutineScope Adds the specified coroutine context to this scope, overriding existing elements in the current scope’s context with the corresponding keys. |
produce | fun <E> CoroutineScope.produce( context: CoroutineContext = EmptyCoroutineContext, capacity: Int = 0, block: suspend ProducerScope<E>.() -> Unit): ReceiveChannel<E> Launches new coroutine to produce a stream of values by sending them to a channel and returns a reference to the coroutine as a ReceiveChannel. This resulting object can be used to receive elements produced by this coroutine. |
- actor{} 會啓動一個能夠接收 Message 的 Coroutine,也就是 SendChannel.你可以通過SendChannel.send() 方法給其它協程發送消息或者結果,或者通過 SendChannel.offer() 發送消息,兩者區別在於 offer() 在隊列滿的時候,會返回失敗,而 send() 則不會。對應接收消息的是 ReceiveChannel。
- async{} 會啓動一個新的協程,並且返回一個 Deferred(它是一個 future 的實現),你可以通過 Deferred.await() 異步獲取結果。
- broadcast{} 和 actor 類似,只不過,BroadcastChannel 是一種一對多的訂閱關係。
- launch{} 啓動一個新的協程,並且返回一個 Job 對象,你可以通過這個 Job 取消協程
- plus 也就是 + ,通過該操作,你可以給該 CoroutineScope 關聯一個 CoroutineContext,並且如果存在同一個 key 會替換之前的 CoroutineContext。
- newConroutinContext 會給當前 CoroutineScop 創建一個 ,如果不存在 Dispatcher 或者 ContinuationInterceptor 則會給這個 Context 關聯默認的 Dispatcher.Default。
- produce{} 啓動一個新的協程,並且返回 receiveChannel。
官方說明鏈接 CoroutineScope
CoroutineContext
協程是運行在 CoroutinesContext 的一些集合裏面,根據官方文檔的意思
* Persistent context for the coroutine. It is an indexed set of [Element] instances.
* An indexed set is a mix between a set and a map.
* Every element in this set has a unique [Key]. Keys are compared _by reference_.
也就是說,CoroutineContext 是 coroutine 的運行的 Context,它是 Element 實例的集合,這種集合介於 set 和map 之間,每一個 Element 都有一個和對象引用相關的 key,作爲 Element 的唯一標誌。
簡單看下其源碼:
public interface CoroutineContext {
/**
* Returns the element with the given [key] from this context or `null`.
* Keys are compared _by reference_, that is to get an element from the context the reference to its actual key
* object must be presented to this function.
通過key 獲得對應的CoroutineContext
*/
public operator fun <E : Element> get(key: Key<E>): 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.
使用 initial 作爲初始值,operation 作爲累加操作
*/
public fun <R> 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 =
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)
}
}
}
/**
* Returns a context containing elements from this context, but without an element with
* the specified [key]. Keys are compared _by reference_, that is to remove an element from the context
* the reference to its actual key object must be presented to this function.
*/
public fun minusKey(key: Key<*>): CoroutineContext
/**
* Key for the elements of [CoroutineContext]. [E] is a type of element with this key.
* Keys in the context are compared _by reference_.
*/
public interface Key<E : Element>
/**
* An element of the [CoroutineContext]. An element of the coroutine context is a singleton context by itself.
*/
public interface Element : CoroutineContext {
/**
* A key of this coroutine context 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
public override fun <R> fold(initial: R, operation: (R, Element) -> R): R =
operation(initial, this)
public override fun minusKey(key: Key<*>): CoroutineContext =
if (this.key == key) EmptyCoroutineContext else this
}
}
簡單介紹下這裏相關的類以及函數:
相關類
- Element:CoroutineContext 的一個元素,任何一個 Coroutine context 都是自己唯一的單例。
- Key: 作爲CoroutineContext 的key,這些 Key 都是通過對象引用比較的,也就是說,同一個對象的key 纔是相同的。這裏也就是提供一種泛型的能力,使子類的CoroutineContext 的key,可以是任何繼承自 CoroutineContext 的類實例。
- CoroutineContext:如之前的介紹
相關函數
- public operator fun get(key: Key): E? 根據給定的 Key 獲取特定的 CoroutineContext
- public fun fold(initial: R, operation: (R, Element) -> R): R 把傳入的 initial 作爲初始變量,通過傳入的 operation 操作,累次操作 Context 中的 Element。
- public operator fun plus(context: CoroutineContext): CoroutineContext 返回一個包含了當前 Context 和傳入的 CoroutineContext 包含的所有的 Element 的一個 Context。如果是兩者都存在的 Element 會被刪除掉。
- public fun minusKey(key: Key<*>): CoroutineContext 根據傳入的 key 返回該Context 存在的但是不包含傳入的 Keys 的或集。
具體怎樣去理解這些內容呢?我們結合 CoroutineContextImpl.kt 文件裏面提供的幾個 Context 來理解一下:
例如 EmptyCoroutineContext
public object EmptyCoroutineContext : CoroutineContext, Serializable {
private const val serialVersionUID: Long = 0
private fun readResolve(): Any = EmptyCoroutineContext
//EmptyCoroutineContext 不包含一個任何一個 Element
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"
}
CoroutineDispatcher
簡單用法
CoroutineDispatcher 每一個CoroutineContext 都包含一個 CoroutineDispatcher ,它設置了對應的協程使用一個或者多個線程,協程調度器可以將協程的執行侷限在指定的線程中,調度它運行在線程池中或讓它不受限的運行。
先來看下具體怎麼使用它吧,下面是一個簡單的例子:
fun dispatchTest() {
runBlocking {
launch {
doLog("in main thread")
}
launch(Dispatchers.Unconfined) {
doLog("in Unconfined thread 1")
}
launch(Dispatchers.Default) {
doLog(" in child thread 2")
}
launch(newSingleThreadContext("child thread3")) {
doLog("in new thread")
}
}
}
然後控制檯輸出如下:
01-12 17:20:48.212 14843-14843/com.yy.yylite.kotlinshare I/Context: in Unconfined thread 1
01-12 17:20:48.213 14843-14950/com.yy.yylite.kotlinshare I/Context: in child thread 2
01-12 17:20:48.215 14843-14843/com.yy.yylite.kotlinshare I/Context: in main thread
01-12 17:20:48.215 14843-14953/com.yy.yylite.kotlinshare I/Context: in new thread
所以我們可以指定一個協程在特定的子線程執行。
如果直接調用 launch,不指定Dispatcher,那麼它使用的是啓動它的 CoroutineScope 的Context 以及 Dispatch。
常見的 Dispatcher 如下:
- Dispatchers.Default:默認的 Dispatcher,會在jvm 層級共享線程池,會創建等於 cpu 核數的線程數目,但是始終大於等於2,因爲 cpu 是非常珍貴的資源所以使用 Default Dispatcher 是非常合理的。
- Dispatchers.IO :用於IO 操作的Dispatcher,是按需創建的線程池。
- Dispatcher.Main:主線程的 Dispatcher
- Dispatcher.UnConfined:不確定的 Dispatcher,會在調用的地方創建 Coroutine,但是會在任意線程執行 Coroutine,取決於第一個掛起的協程的返回結果。
關於 Dispatcher.UnConfined 我們怎麼去理解呢?先看一個例子:
/**
* 非限定的 Dispatcher
*/
fun testDispatcher() = runBlocking {
launch(Dispatchers.Unconfined) {
doLog("start coroutine")
delay(500)
doLog("after delay")
}
launch {
doLog("in main start coroutine ")
delay(1000)
doLog("in main after delay")
}
}
然後控制檯輸出結果如下:
01-19 10:22:19.785 30584-30584/com.yy.yylite.kotlinshare I/Context: start coroutine
01-19 10:22:19.788 30584-30584/com.yy.yylite.kotlinshare I/Context: in main start coroutine
01-19 10:22:20.286 30584-30723/com.yy.yylite.kotlinshare I/Context: after delay
01-19 10:22:20.788 30584-30584/com.yy.yylite.kotlinshare I/Context: in main after delay
也就是說,Unconfined 類型的 Dispatcher最終分發的線程,是不確定的。
源碼分析
協程執行在特定的 CoroutineContext,而CoroutineContext 中總是有一個特定的 Dispatcher,負責分發協程,根據官網對 CoroutineDispatcher 的說明如下:
Coroutine context includes a coroutine dispatcher (see [CoroutineDispatcher]) that determines what thread or threads
the corresponding coroutine uses for its execution. Coroutine dispatcher can confine coroutine execution
to a specific thread, dispatch it to a thread pool, or let it run unconfined.
簡單來說,CoroutineDispatcher 決定了當前 Coroutine 在哪個線程或者哪幾個線程中執行,可能是某個特定的線程,也可能是分發到線程池或者是 unconfined。
繼承結構
public abstract class CoroutineDispatcher :
AbstractCoroutineContextElement(ContinuationInterceptor), ContinuationInterceptor {}
也就是說 CoroutineDispatcher 既是一種 Element 也是 ContinuationInterceptor。
其存在多個子類,如下:
其中 ExecutorCoroutineDispatcher 是基於線程池 Executor 來分發任務的,並且每一個線程池,需要Dispatcher 自己本身去關閉。
MainCoroutinCoroutineDispatcher 是在主線程分發的特殊 Dispatcher,你可以通過 Dispatcher.Mian 獲得,它是一個 Object,可以被直接訪問。
關鍵函數分析
/**
* 是否需要將協程執行分發到其它線程,如果是 true 則表示需要,false 表示不需要;一般都是返回 true
*/
public open fun isDispatchNeeded(context: CoroutineContext): Boolean = true
/**
* Dispatches execution of a runnable [block] onto another thread in the given [context].
將協程的執行代碼,也就是 Runnable 分發到指定 Context 的線程
*/
public abstract fun dispatch(context: CoroutineContext, block: Runnable)
/**
* Returns continuation that wraps the original [continuation], thus intercepting all resumptions.
返回一封裝了原始 continuation 的 continuation,實現可以攔截所有 Coroutin 的 resumtions
*/
public final override fun <T> interceptContinuation(continuation: Continuation<T>): Continuation<T> =
DispatchedContinuation(this, continuation)
從上面的介紹,我們可以大概的知道,dispatch() 類似java Executor 裏面的 void execute(Runnable command);方法,幫助執行子線程的方法。
ExecutorCoroutineDispatcher
ExecutorCoroutineDispatcher 是 CoroutineDispatcher 的子類,主要用於在子線程分發 Coroutine 的場景,源碼比較簡單,直接看吧:
public abstract class ExecutorCoroutineDispatcher: CoroutineDispatcher(), Closeable {
/**
* Closes this coroutine dispatcher and shuts down its executor.
*
* It may throw an exception if this dispatcher is global and cannot be closed.
*/
public abstract override fun close()
/**
* Underlying executor of current [CoroutineDispatcher].
*/
public abstract val executor: Executor
}
這裏包含了一個 Executor 線程池類的變量,然後就是定義了一個 close() 方法,用於關閉線程池。需要注意的是,該類間接繼承了 CoroutineContext 類,所以存在 cancel() 以及 cancelChildren() 方法,用於取消當前 Job 或者該 Context 的Children Job。
該類的子類是 ExecutorCoroutineDispatcherBase,看下面分析。
ExecutorCoroutineDispatcherBase
繼承結構
internal abstract class ExecutorCoroutineDispatcherBase : ExecutorCoroutineDispatcher(), Delay {}
除了繼承ExecutorCoroutineDispatcher 之外,還實現了Delay 接口,Delay 接口如下:
public interface Delay {
/**
掛起協程,並且不會阻塞當前線程
*/
suspend fun delay(time: Long) {
if (time <= 0) return // don't delay
return suspendCancellableCoroutine { scheduleResumeAfterDelay(time, it) }
}
/**
*/
fun scheduleResumeAfterDelay(timeMillis: Long, continuation: CancellableContinuation<Unit>)
/**
*/
fun invokeOnTimeout(timeMillis: Long, block: Runnable): DisposableHandle =
DefaultDelay.invokeOnTimeout(timeMillis, block)
}
Delay 接口,使得Dispatcher 具備任務調度的能力,例如 delay(time: Long),可以掛起協程,並且不會阻塞當前線程。
直接看ExecutorCoroutineDispatcherBase源碼:
//internal 修飾,表示包內可見
internal abstract class ExecutorCoroutineDispatcherBase : ExecutorCoroutineDispatcher(), Delay {
private var removesFutureOnCancellation: Boolean = false
internal fun initFutureCancellation() {
removesFutureOnCancellation = removeFutureOnCancel(executor)
}
override fun dispatch(context: CoroutineContext, block: Runnable) {
try {
executor.execute(timeSource.wrapTask(block))
} catch (e: RejectedExecutionException) {
timeSource.unTrackTask()
DefaultExecutor.enqueue(block)
}
}
/*
* removesFutureOnCancellation is required to avoid memory leak.
* On Java 7+ we reflectively invoke ScheduledThreadPoolExecutor.setRemoveOnCancelPolicy(true) and we're fine.
* On Java 6 we're scheduling time-based coroutines to our own thread safe heap which supports cancellation.
*/
override fun scheduleResumeAfterDelay(timeMillis: Long, continuation: CancellableContinuation<Unit>) {
val future = if (removesFutureOnCancellation) {
scheduleBlock(ResumeUndispatchedRunnable(this, continuation), timeMillis, TimeUnit.MILLISECONDS)
} else {
null
}
// If everything went fine and the scheduling attempt was not rejected -- use it
if (future != null) {
continuation.cancelFutureOnCancellation(future)
return
}
// Otherwise fallback to default executor
DefaultExecutor.scheduleResumeAfterDelay(timeMillis, continuation)
}
override fun invokeOnTimeout(timeMillis: Long, block: Runnable): DisposableHandle {
val future = if (removesFutureOnCancellation) {
scheduleBlock(block, timeMillis, TimeUnit.MILLISECONDS)
} else {
null
}
return if (future != null ) DisposableFutureHandle(future) else DefaultExecutor.invokeOnTimeout(timeMillis, block)
}
private fun scheduleBlock(block: Runnable, time: Long, unit: TimeUnit): ScheduledFuture<*>? {
return try {
(executor as? ScheduledExecutorService)?.schedule(block, time, unit)
} catch (e: RejectedExecutionException) {
null
}
}
override fun close() {
(executor as? ExecutorService)?.shutdown()
}
override fun toString(): String = executor.toString()
override fun equals(other: Any?): Boolean = other is ExecutorCoroutineDispatcherBase && other.executor === executor
override fun hashCode(): Int = System.identityHashCode(executor)
}
首先看下 dispatch() 方法:
override fun dispatch(context: CoroutineContext, block: Runnable) {
try {
executor.execute(timeSource.wrapTask(block))
} catch (e: RejectedExecutionException) {
timeSource.unTrackTask()
DefaultExecutor.enqueue(block)
}
}
直接調用了 executor.execute() 執行封裝好的 Runnable,也就是之前通過 launch 或者 async 傳入的代碼塊封裝的,不過這之前會先用 timeSource.wrapTask(block),包裹一下這個 Runnable,那麼在 wrapTask() 裏面做了什麼操作?其實,沒做啥,相反,我們看到了其它一些代碼:
internal object DefaultTimeSource : TimeSource {
override fun currentTimeMillis(): Long = System.currentTimeMillis()
override fun nanoTime(): Long = System.nanoTime()
override fun wrapTask(block: Runnable): Runnable = block
override fun trackTask() {}
override fun unTrackTask() {}
override fun registerTimeLoopThread() {}
override fun unregisterTimeLoopThread() {}
override fun parkNanos(blocker: Any, nanos: Long) {
LockSupport.parkNanos(blocker, nanos)
}
override fun unpark(thread: Thread) {
LockSupport.unpark(thread)
}
}
internal var timeSource: TimeSource = DefaultTimeSource
也就是說,Coroutine 提供的阻塞功能是通過LockSupport 掛起的。
CoroutineDispatcher 選擇
創建 Dispatcher 的時候會創建一個 CoroutineScheduler,代碼如下:
//第一次調用 Dispatcher.Default 會執行此方法
public actual val Default: CoroutineDispatcher = createDefaultDispatcher()
//接着會根據useCoroutinesScheduler 創建 DefaultScheduler 或者 CommonPool
internal actual fun createDefaultDispatcher(): CoroutineDispatcher =
if (useCoroutinesScheduler) DefaultScheduler else CommonPool
//接着這裏創建 CoroutineScheduler,既是一個 Dispatcher 也是一個 Executor
private fun createScheduler() = CoroutineScheduler(corePoolSize, maxPoolSize, idleWorkerKeepAliveNs, schedulerName)
這裏存在兩種 Dispatcher(也是線程池Executor),那麼各自存在什麼特點同時又有什麼不同呢?
CoroutineStart
通過 launch{} 等創建一個協程的時候,你也可以傳入一個 CoroutineStart 枚舉值,這個枚舉值參數定義了 CoroutineBuilder 的執行 Coroutine 的時機,具體的時機由以下幾種:
- DEFAULTE:會根據該 Coroutine 依賴的 Context 立刻執行該 Coroutine
- LAZY:按需執行 Coroutine,僅僅在你調用了 Job.start() 或者 Job.await() 之後會執行
- ATOMIC: 原子操作類型,也就是說會根據依賴的Context 執行 Coroutine,但是該 Coroutine 不可取消。對比 DEFAULT 類型,它不不可取消,不能通過 job.cancel() 去取消的。
- UNDISPATCHED:會在當前線程第一個掛起點執行 Coroutine
關於 Lazy 模式,簡單例子如下:
fun testLazy() {
doLog("testLazy")
val job = GlobalScope.launch(start = CoroutineStart.LAZY) {
doLog("I'm begin")
delay(1000)
doLog("I'm finish")
}
runBlocking {
delay(1000)
doLog("Job start")
job.start()
}
}
設置該 launch 爲 CoroutineStart 爲 Lazy,則 Coroutine 會在 Job.start() 之後執行。
輸入結果如下:
01-14 12:30:51.128 22558-22558/com.yy.yylite.kotlinshare I/Context: testLazy
01-14 12:30:52.137 22558-22558/com.yy.yylite.kotlinshare I/Context: Job start
01-14 12:30:52.139 22558-22607/com.yy.yylite.kotlinshare I/Context: I'm begin
01-14 12:30:53.142 22558-22609/com.yy.yylite.kotlinshare I/Context: I'm finish
簡單看下源碼:
//launch{} 源碼中 Coroutine.launch()
val coroutine = if (start.isLazy)
LazyStandaloneCoroutine(newContext, block) else
StandaloneCoroutine(newContext, active = true)
//Builder.common.kt 文件中
private open class StandaloneCoroutine(
parentContext: CoroutineContext,
active: Boolean
) : AbstractCoroutine<Unit>(parentContext, active) {
override val cancelsParent: Boolean get() = true
override fun handleJobException(exception: Throwable) = handleExceptionViaHandler(parentContext, exception)
}
private class LazyStandaloneCoroutine(
parentContext: CoroutineContext,
block: suspend CoroutineScope.() -> Unit
) : StandaloneCoroutine(parentContext, active = false) {
private var block: (suspend CoroutineScope.() -> Unit)? = block
override fun onStart() {
val block = checkNotNull(this.block) { "Already started" }
this.block = null
block.startCoroutineCancellable(this, this)
}
}
也就是說,默認創建的是 StandaloneCoroutine(newContext,active = true),Lazy 模式創建的是 LazyStandaloneCoroutine(newContext, block) 其 active 是 false。
CoroutineExceptionHandler
/**
* An optional element in the coroutine context to handle uncaught exceptions.
*
* Normally, uncaught exceptions can only result from coroutines created using [launch][CoroutineScope.launch] builder.
* A coroutine that was created using [async][CoroutineScope.async] always catches all its exceptions and represents them
* in the resulting [Deferred] object.
*
* By default, when no handler is installed, uncaught exception are handled in the following way:
* * If exception is [CancellationException] then it is ignored
* (because that is the supposed mechanism to cancel the running coroutine)
* * Otherwise:
* * if there is a [Job] in the context, then [Job.cancel] is invoked;
* * Otherwise, all instances of [CoroutineExceptionHandler] found via [ServiceLoader]
* * and current thread's [Thread.uncaughtExceptionHandler] are invoked.
**/
默認的情況下,使用 launch{} 會默認使用一個 CoroutineExceptionHandler,但是如果是 async 的話,你會通過 Deferred 獲得失敗的情況。
默認情況下,CoroutineExceptionHandler 會處理一些異常情況。
下一章節,我會介紹一下 launch 的原理和流程