Kotlin协程:创建、启动、挂起,Android开发知识体系

launch方法的代码如下:


// 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

}



newCoroutineContext用于计算新的上下文,代码如下:


public actual fun CoroutineScope.newCoroutineContext(context: CoroutineContext): CoroutineContext {

    // coroutineContext为CoroutineScope中保存的全局变量

    // 对上下文进行相加

    val combined = coroutineContext + context

    // 用于debug

    val debug = if (DEBUG) combined + CoroutineId(COROUTINE_ID.incrementAndGet()) else combined

    // 如果上下文中没有调度器,则添加一个默认的调度器

    return if (combined !== Dispatchers.Default && combined[ContinuationInterceptor] == null)

        debug + Dispatchers.Default else debug

}



1.start方法


在不指定协程启动模式的情况下,协程将按照DEFAULT模式启动,在上述代码中,会调用StandaloneCoroutine对象的start方法。StandaloneCoroutine的代码如下:


private open class StandaloneCoroutine(

    parentContext: CoroutineContext,

    active: Boolean

) : AbstractCoroutine<Unit>(parentContext, active) {

    override fun handleJobException(exception: Throwable): Boolean {

        handleCoroutineException(context, exception)

        return true

    }

}



StandaloneCoroutine类中仅重写了handleJobException方法,用于处理父协程不处理的异常。因此这里调用的start方法实际是父类AbstractCoroutine的方法,AbstractCoroutine类的start方法代码如下:


public fun <R> start(start: CoroutineStart, receiver: R, block: suspend R.() -> T) {

    // 该方法用于完成父协程与子协程的绑定关联,同时确保父协程启动

    initParentJob()

    // 该方法的写法等同于start.invoke(block, receiver, this)

    // 因此调用的CoroutineStart类的方法

    start(block, receiver, this)

}



AbstractCoroutine类的start方法内,调用了CoroutineStart类的invoke方法。

2. CoroutineStart类


CoroutineStart是一个枚举类,用于根据不同的启动模式去启动协程,代码如下:


public enum class CoroutineStart {

    // 四种启动模式

    DEFAULT,



    LAZY,

    

    // 具有实验性,慎用

    @ExperimentalCoroutinesApi

    ATOMIC,

    

    // 具有实验性,慎用

    @ExperimentalCoroutinesApi

    UNDISPATCHED;



    // 根据不同的启动策略,启动协程,执行block

    @InternalCoroutinesApi

    public operator fun <T> invoke(block: suspend () -> T, completion: Continuation<T>): Unit =

        when (this) {

            DEFAULT -> block.startCoroutineCancellable(completion)

            ATOMIC -> block.startCoroutine(completion)

            UNDISPATCHED -> block.startCoroutineUndispatched(completion)

            LAZY -> Unit // 该模式不主动启动,等待用户调用start方法

        }



    // 根据不同的启动策略,启动协程,执行block

    @InternalCoroutinesApi

    public operator fun <R, T> invoke(block: suspend R.() -> T, receiver: R, completion: Continuation<T>): Unit =

        when (this) {

            DEFAULT -> block.startCoroutineCancellable(receiver, completion)

            ATOMIC -> block.startCoroutine(receiver, completion)

            UNDISPATCHED -> block.startCoroutineUndispatched(receiver, completion)

            LAZY -> Unit

        }



    // 当前的启动模式是否为懒启动

    @InternalCoroutinesApi

    public val isLazy: Boolean get() = this === LAZY

}



CoroutineStart类中有两个invoke方法,其中一个参数中有receiver,另一个没有receiver。在Kotlin协程中,很多方法都重载了带有receiver的方法和不带有receiver的方法。

receiver用于为block执行提供一个环境。Kotlin中提供的启动协程的方法都是通过带receiver参数的start方法实现。通过receiver环境,可以更方便的实现一些操作,比如在launch启动的协程中再次调用launch启动新的协程。在没有receiver的环境下执行block,则更像是在suspend方法中执行,如果需要启动其他的协程,需要自己提供环境。

3. startCoroutineCancellable方法


startCoroutineCancellable是一个扩展方法,用来创建一个可以取消的协程,代码如下:


internal fun <R, T> (suspend (R) -> T).startCoroutineCancellable(receiver: R, completion: Continuation<T>) =

    runSafely(completion) {

        // createCoroutineUnintercepted:创建协程

        // intercepted:拦截调度

        // resumeCancellableWith:恢复执行

        createCoroutineUnintercepted(receiver, completion).intercepted().resumeCancellableWith(Result.success(Unit))

    }



// 如果创建的过程发生异常,则通知续体恢复后续代码的执行

private inline fun runSafely(completion: Continuation<*>, block: () -> Unit) {

    try {

        block()

    } catch (e: Throwable) {

        completion.resumeWith(Result.failure(e))

    }

}



4.createCoroutineUnintercepted方法


createCoroutineUnintercepted方法用于创建一个新的、可挂起的、不受干扰的协程。


public expect fun <R, T> (suspend R.() -> T).createCoroutineUnintercepted(

    receiver: R,

    completion: Continuation<T>

): Continuation<Unit>



在Kotlin中有很多被expect关键字标记的接口方法,需要找到对应平台下被actual标记的实现方法。


public actual fun <R, T> (suspend R.() -> T).createCoroutineUnintercepted(

    receiver: R,

    completion: Continuation<T>

): Continuation<Unit> {

    // 用于debug

    val probeCompletion = probeCoroutineCreated(completion)

    return if (this is BaseContinuationImpl)

        create(receiver, probeCompletion)

    else {

        createCoroutineFromSuspendFunction(probeCompletion) {

            (this as Function2<R, Continuation<T>, Any?>).invoke(receiver, it)

        }

    }

}



createCoroutineUnintercepted方法创建的协程需要手动调用resumeWith方法才可以启动,但重复的调用resumeWith方法可能会导致状态机发生异常。同时,参数中传入的completion可能会在任意的上下文中被调用。

正常情况下,我们编写的lambda表达式——block,在编译器编译时,会自动生成一个类,并继承SuspendLambda类,实现Continuation等接口。因为SuspendLambda继承自ContinuationImpl,ContinuationImpl继承自BaseContinuationImpl,所以才有了上述代码中的判断逻辑。

如果当前的block对象的类型为BaseContinuationImpl,则调用create方法,这里的create方法是编译器生成的类里的重写方法,它的内部就是通过我们传入的参数,创建并返回根据blcok生成的类的一个实例对象。

如果当前的block对象的类型不为BaseContinuationImpl,则需要通过createCoroutineFromSuspendFunction方法创建协程。这里假设lambda表达式的类型不是BaseContinuationImpl。

5.createCoroutineFromSuspendFunction方法


该方法用于在createCoroutineUnintercepted方法中使用,当一个被suspend修饰的lambda表达式没有继承BaseContinuationImpl类时,则通过此方法创建协程。

有两种情况会调用该方法创建协程:第一种情况是lambda表达式中调用了其他的挂起方法;第二种情况是挂起方法是通过Java实现的。

createCoroutineFromSuspendFunction方法的代码如下:


private inline fun <T> createCoroutineFromSuspendFunction(

    completion: Continuation<T>,

    crossinline block: (Continuation<T>) -> Any?

): Continuation<Unit> {

    val context = completion.context

    // 如果上下文为空

    return if (context === EmptyCoroutineContext)

        // 创建一个受限协程

        object : RestrictedContinuationImpl(completion as Continuation<Any?>) {

            private var label = 0



            override fun invokeSuspend(result: Result<Any?>): Any? =

                when (label) {

                    0 -> {

                        label = 1

                        result.getOrThrow()

                        block(this)

                    }

                    1 -> {

                        label = 2

                        result.getOrThrow()

                    }

                    else -> error("This coroutine had already completed")

                }

        }

    else // 不为空,则创建一个正常的协程

        object : ContinuationImpl(completion as Continuation<Any?>, context) {

            private var label = 0



            override fun invokeSuspend(result: Result<Any?>): Any? =

                when (label) {

                    0 -> {

                        label = 1

                        result.getOrThrow()

                        block(this)

                    }

                    1 -> {

                        label = 2

                        result.getOrThrow()

                    }

                    else -> error("This coroutine had already completed")

                }

        }

}



受限协程是指协程在运行过程中的,只能调用协程作用域中提供的挂起方法发生挂起,其他挂起方法不能调用,因为在挂起方法会对续体进行拦截,可能导致后续代码的执行变得无法预测。

典型的例子就是sequence方法,它创建的协程就是受限协程,只能通过调用yield方法或者yieldAll方法才能发生挂起。由于受限协程中不能进行协程调度,因此其上下文是空的。

这里launch方法的上下文有一个默认调度器,因此会创建一个ContinuationImpl对象。

到这里,协程完成了创建。

二.协程的启动

=========================================================================

再次回到startCoroutineCancellable方法,当调用createCoroutineUnintercepted创建好协程后,会调用intercepted方法,代码如下:


public actual fun <T> Continuation<T>.intercepted(): Continuation<T> =

    (this as? ContinuationImpl)?.intercepted() ?: this



intercepted方法是Continuation接口的扩展方法,内部调用了ContinuationImpl类的intercepted方法。

1.ContinuationImpl类



internal abstract class ContinuationImpl(

    completion: Continuation<Any?>?,

    private val _context: CoroutineContext?

) : BaseContinuationImpl(completion) {

    constructor(completion: Continuation<Any?>?) : this(completion, completion?.context)



    public override val context: CoroutineContext

        get() = _context!!



    @Transient

    private var intercepted: Continuation<Any?>? = null



    // 如果没有缓存,则从上下文中获取拦截器,调用interceptContinuation进行拦截,

    // 将拦截的续体保存到全局变量

    public fun intercepted(): Continuation<Any?> =

        intercepted

            ?: (context[ContinuationInterceptor]?.interceptContinuation(this) ?: this)

                .also { intercepted = it }



    protected override fun releaseIntercepted() {

        val intercepted = intercepted

        if (intercepted != null && intercepted !== this) {

            context[ContinuationInterceptor]!!.releaseInterceptedContinuation(intercepted)

        }

        this.intercepted = CompletedContinuation // just in case

    }

}



这里的ContinuationInterceptor指的就是在newCoroutineContext方法中传入的Dispatchers.Default调度器。CoroutineDispatcher类的interceptContinuation方法的代码如下:


public abstract class CoroutineDispatcher :

    AbstractCoroutineContextElement(ContinuationInterceptor), ContinuationInterceptor {

     ...

    // 将续体包裹成DispatchedContinuation,并传入当前调度器 

    public final override fun <T> interceptContinuation(continuation: Continuation<T>): Continuation<T> =

        DispatchedContinuation(this, continuation)

    ...

}



2.resumeCancellableWith方法


再次回到startCoroutineCancellable方法,当调用intercepted方法进行拦截后,会调用resumeCancellableWith方法,代码如下:


public fun <T> Continuation<T>.resumeCancellableWith(result: Result<T>): Unit = when (this) {

    is DispatchedContinuation -> resumeCancellableWith(result)

    else -> resumeWith(result)

}



由于当前的Continuation对象的类型为DispatchedContinuation,因此调用DispatchedContinuation类的resumeCancellableWith方法,代码如下:


internal class DispatchedContinuation<in T>(

    @JvmField val dispatcher: CoroutineDispatcher,

    @JvmField val continuation: Continuation<T>

) : DispatchedTask<T>(MODE_ATOMIC_DEFAULT), CoroutineStackFrame, Continuation<T> by continuation {

    

    ...

    

    @Suppress("NOTHING_TO_INLINE")

    inline fun resumeCancellableWith(result: Result<T>) {

        val state = result.toState()

        // 是否进行调度

        if (dispatcher.isDispatchNeeded(context)) {

            _state = state

            resumeMode = MODE_CANCELLABLE

            // 进行调度

            dispatcher.dispatch(context, this)


**自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。**

**深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!**

**因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。**
![img](https://img-blog.csdnimg.cn/img_convert/f1e1112191c9592d0d04992a9190fb2c.png)
![img](https://img-blog.csdnimg.cn/img_convert/2d314cb6255379dbc712cfce8359df25.png)
![img](https://img-blog.csdnimg.cn/img_convert/c6cd325a922f784d4754b8a36663cde9.png)
![img](https://img-blog.csdnimg.cn/img_convert/cc3f590f77ccd0941fe501d42a52c43a.png)
![img](https://img-blog.csdnimg.cn/img_convert/24fa74800deae06ee4afef2d620e9fb9.png)
![img](https://img-blog.csdnimg.cn/img_convert/2764f11c044c6c58fd644e4426ea716a.png)
![img](https://img-blog.csdnimg.cn/13f2cb2e05a14868a3f0fd6ac81d625c.png)

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!**

**由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新**

**如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)**
![img](https://img-blog.csdnimg.cn/img_convert/000c810c75ab5c34afbaa702ca095bb5.png)



### 题外话


我们见过很多技术leader在面试的时候,遇到处于迷茫期的大龄程序员,比面试官年龄都大。这些人有一些共同特征:可能工作了7、8年,还是每天重复给业务部门写代码,工作内容的重复性比较高,没有什么技术含量的工作。问到这些人的职业规划时,他们也没有太多想法。

其实30岁到40岁是一个人职业发展的黄金阶段,一定要在业务范围内的扩张,技术广度和深度提升上有自己的计划,才有助于在职业发展上有持续的发展路径,而不至于停滞不前。

不断奔跑,你就知道学习的意义所在!


> **注意:我们之前因为秋招收集的二十套一二线互联网公司Android面试真题(含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)**

![](https://img-blog.csdnimg.cn/img_convert/77ea3aff36c82e1a322c820fb972a5c5.webp?x-oss-process=image/format,png)

> **本文已被[CODING开源项目:《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》]( )收录**

[**一个人可以走的很快,但一群人才能走的更远。如果你从事以下工作或对以下感兴趣,欢迎戳这里加入程序员的圈子,让我们一起学习成长!**](https://bbs.csdn.net/forums/4304bb5a486d4c3ab8389e65ecb71ac0)

**AI人工智能、Android移动开发、AIGC大模型、C C#、Go语言、Java、Linux运维、云计算、MySQL、PMP、网络安全、Python爬虫、UE5、UI设计、Unity3D、Web前端开发、产品经理、车载开发、大数据、鸿蒙、计算机网络、嵌入式物联网、软件测试、数据结构与算法、音视频开发、Flutter、IOS开发、PHP开发、.NET、安卓逆向、云计算**

复性比较高,没有什么技术含量的工作。问到这些人的职业规划时,他们也没有太多想法。

其实30岁到40岁是一个人职业发展的黄金阶段,一定要在业务范围内的扩张,技术广度和深度提升上有自己的计划,才有助于在职业发展上有持续的发展路径,而不至于停滞不前。

不断奔跑,你就知道学习的意义所在!


> **注意:我们之前因为秋招收集的二十套一二线互联网公司Android面试真题(含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)**

[外链图片转存中...(img-0EzED0ct-1712198294733)]

> **本文已被[CODING开源项目:《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》]( )收录**

[**一个人可以走的很快,但一群人才能走的更远。如果你从事以下工作或对以下感兴趣,欢迎戳这里加入程序员的圈子,让我们一起学习成长!**](https://bbs.csdn.net/forums/4304bb5a486d4c3ab8389e65ecb71ac0)

**AI人工智能、Android移动开发、AIGC大模型、C C#、Go语言、Java、Linux运维、云计算、MySQL、PMP、网络安全、Python爬虫、UE5、UI设计、Unity3D、Web前端开发、产品经理、车载开发、大数据、鸿蒙、计算机网络、嵌入式物联网、软件测试、数据结构与算法、音视频开发、Flutter、IOS开发、PHP开发、.NET、安卓逆向、云计算**

  • 13
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值