kotlin coroutines 協程教程(二)關鍵類分析

原理篇(一)關鍵類的分析

上面簡單的介紹了一些用法,但是具體的原理和特點,好像還不是很清楚,那麼下面就來介紹一下,一些關鍵的類,流程和原理。

介紹的相關的原理基於這行代碼:

    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(): UnitCancels 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): JobLaunches 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): CoroutineContextCreates 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): CoroutineScopeAdds 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
    }
}

簡單介紹下這裏相關的類以及函數:

相關類

  1. Element:CoroutineContext 的一個元素,任何一個 Coroutine context 都是自己唯一的單例。
  2. Key: 作爲CoroutineContext 的key,這些 Key 都是通過對象引用比較的,也就是說,同一個對象的key 纔是相同的。這裏也就是提供一種泛型的能力,使子類的CoroutineContext 的key,可以是任何繼承自 CoroutineContext 的類實例。
  3. CoroutineContext:如之前的介紹

相關函數

  1. public operator fun get(key: Key): E? 根據給定的 Key 獲取特定的 CoroutineContext
  2. public fun fold(initial: R, operation: (R, Element) -> R): R 把傳入的 initial 作爲初始變量,通過傳入的 operation 操作,累次操作 Context 中的 Element。
  3. public operator fun plus(context: CoroutineContext): CoroutineContext 返回一個包含了當前 Context 和傳入的 CoroutineContext 包含的所有的 Element 的一個 Context。如果是兩者都存在的 Element 會被刪除掉。
  4. 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 的原理和流程

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章