Kotlin协程详解——协程作用域CoroutineScope

一、源码分析

public interface CoroutineScope {
    /**
     * The context of this scope.
     * Context is encapsulated by the scope and used for implementation of coroutine builders that are extensions on the scope.
     * Accessing this property in general code is not recommended for any purposes except accessing the [Job] instance for advanced usages.
     *
     * By convention, should contain an instance of a [job][Job] to enforce structured concurrency.
     */
    public val coroutineContext: CoroutineContext
}

CoroutineScope中只包含一个待实现的变量CoroutineContext,至于CoroutineContext之前的文章已经分析了它的内部结构,这里就不再累赘了。

通过它的结构,我们可以认为它是提供CoroutineContext的容器,保证CoroutineContext能在整个协程运行中传递下去,约束CoroutineContext的作用边界。

例如,在Android中使用协程来请求数据,当接口还没有请求完成时Activity就已经退出了,这时如果不停止正在运行的协程将会造成不可预期的后果。所以在Activity中我们都推荐使用lifecycleScope来启动协程,lifecycleScope可以让协程具有与Activity一样的生命周期意识。

下面是lifecycleScope源码:

val LifecycleOwner.lifecycleScope: LifecycleCoroutineScope
    get() = lifecycle.coroutineScope
    
val Lifecycle.coroutineScope: LifecycleCoroutineScope
    get() {
        while (true) {
            val existing = mInternalScopeRef.get() as LifecycleCoroutineScopeImpl?
            if (existing != null) {
                return existing
            }
            val newScope = LifecycleCoroutineScopeImpl(
                this,
                SupervisorJob() + Dispatchers.Main.immediate
            )
            if (mInternalScopeRef.compareAndSet(null, newScope)) {
                newScope.register()
                return newScope
            }
        }
    }

它创建了一个LifecycleCoroutineScopeImpl实例,它实现了CoroutineScope接口,同时传入SupervisorJob() + Dispatchers.Main作为它的CoroutineContext

我们再来看它的register()方法

internal class LifecycleCoroutineScopeImpl(
    override val lifecycle: Lifecycle,
    override val coroutineContext: CoroutineContext
) : LifecycleCoroutineScope(), LifecycleEventObserver {
    init {
        // in case we are initialized on a non-main thread, make a best effort check before
        // we return the scope. This is not sync but if developer is launching on a non-main
        // dispatcher, they cannot be 100% sure anyways.
        if (lifecycle.currentState == Lifecycle.State.DESTROYED) {
            coroutineContext.cancel()
        }
    }
 
    fun register() {
        // TODO use Main.Immediate once it is graduated out of experimental.
        launch(Dispatchers.Main) {
            if (lifecycle.currentState >= Lifecycle.State.INITIALIZED) {
                lifecycle.addObserver(this@LifecycleCoroutineScopeImpl)
            } else {
                coroutineContext.cancel()
            }
        }
    }
 
    override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
        if (lifecycle.currentState <= Lifecycle.State.DESTROYED) {
            lifecycle.removeObserver(this)
            coroutineContext.cancel()
        }
    }

register方法中通过经典的launch来创建一个协程,而launch使用到的CoroutineContext就是CoroutineSope中的CoroutineContext。然后在协程中结合JetpackLifecycle特性来监听Activiyt的生命周期。

意思就是说在Activity销毁的时候会调用下面的方法取消协程的运行。

coroutineContext.cancel()

这里就使用到了CoroutineContext,经过上篇文章的分析我们很容易知道CoroutineContext自身是没有cancel方法的,所以这个cancel方法是CoroutineContext的扩展方法。

public fun CoroutineContext.cancel(): Unit {
    this[Job]?.cancel()
}

所以真正的逻辑是从CoroutineContex集合中取出KeyJob的实例,这个对应的就是上面创建LifecycleCoroutineScopeImpl实例时传入的SupervisorJob,它是CoroutineContext的其中一个子类。

这时再来看lifecycleScope相关的一些方法

lifecycleScope.launchWhenCreated {  }
lifecycleScope.launchWhenStarted {  }
lifecycleScope.launchWhenResumed {  }

这些方法的内部逻辑就很明显了,也就是通过Lifecycle来追踪Activity的生命周期,从而约束协程运行的时机。

我们也可以不使用lifecycleScope,自己实现一个CoroutineScope,让它在Activity达到同样的效果。

 class MyActivity : AppCompatActivity(), CoroutineScope {
     lateinit var job: Job
     override val coroutineContext: CoroutineContext
         get() = Dispatchers.Main + job

     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
         job = Job()
     }
 
     override fun onDestroy() {
         super.onDestroy()
         job.cancel() // Cancel job on activity destroy. After destroy all children jobs will be cancelled automatically
     }
 
     /*
      * Note how coroutine builders are scoped: if activity is destroyed or any of the launched coroutines
      * in this method throws an exception, then all nested coroutines are cancelled.
      */
     fun loadDataFromUI() = launch { // <- extension on current activity, launched in the main thread
        val ioData = async(Dispatchers.IO) { // <- extension on launch scope, launched in IO dispatcher
            // blocking I/O operation
        }
        // do something else concurrently with I/O
        val data = ioData.await() // wait for result of I/O
        draw(data) // can draw in the main thread
     }
 }

上面的实现也能够保证当前Activiyt中的协程在Activity销毁的时候终止协程的运行。

到这里CoroutineScope的作用就呼之欲出了,它就是用来约束协程的边界,能够很好的提供对应的协程取消功能,保证协程的运行范围。

二、分类

常见的 CoroutineScope 协程作用域 :

  • GlobalScope : 该作用域是 进程级别的 , 与应用进程同级 , 即使 Activity 被销毁 , 协程任务也可以继续执行 ;
  • MainScope : 该 作用域仅在 Activty 中 , 如果 Activity 被销毁 , 则 在 onDestory 生命周期函数中取消协程任务 ;
  • viewModelScope : 该作用与仅在 ViewModel 中使用 , 与 ViewModel 生命周期绑定 ;
  • lifecycleScope : 该作用与仅在 Activity 中使用 , 与 Activity 生命周期绑定 ;

推荐文章

Kotlin协程实现原理:CoroutineScope&Job

https://juejin.cn/post/7388399895509598260

globalScope、corountineScope和viewScope在Kotlin中的区别 - 腾讯云开发者社区 - 腾讯云

Kotlin教程 协程-作用域_kotlin协程作用域-优快云博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

闲暇部落

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值