作用域CoroutineScope

  • 1、作用域概括
  • 2、普通函数
  • 3、作用域CoroutineScope
  • 4、扩展函数/扩展属性
    • ①、launch、async
    • ②、join、await
    • ③、其他扩展
      • <1>、cancle()
      • <2>、ensureActive()、isActive
      • <3>、plus()
  • 5、实现类
    • ①、核心库
    • ②、平台支持
    • ③、工厂函数
  • 6、协程作用域函数
    • ①、coroutineScope( )
    • ②、supervisorScope( )
    • ③、withContext( )
    • ④、withTimeout( )
    • ⑤、withTimeoutOrNull( )
  • 7、创建协程的区别

1、作用域概括

从思维脑图你应该知道的事:

  • 作用域CoroutineScope是一个接口,但是他有一个CoroutineScope工厂函数可以自定义作用域这个不要搞混了
  • 作用域CoroutineScope有扩展函数、扩展属性意味着所有的实现类、工厂函数等都可以调用这些扩展函数、属性
  • 挂起函数只能在挂起函数或者作用域中调用,所以协程作用域挂起函数都只能在作用域中调用
  • 协程作用域函数只能构建子协程(不考虑runBlocking,这个函数只做测试)
  • 协程构建器能创建根协程或子协程

2、普通函数

普通函数 runBlocking()

public fun <T> runBlocking(context: CoroutineContext = EmptyCoroutineContext, block: suspend CoroutineScope.() -> T): T {//......
}
  • 会阻塞当前线程直到其内部所有协程执行完毕,内部的协程彼此之间依旧是非阻式。
  • 用于把阻塞式的普通函数内部改为协程式编写。

注意:会阻塞线程在开发中不会使用,一般用于main函数作改成协程或者测试函数改成协程进行测试,目的就是函数内部提供一个协程作用域

@Test
fun coroutineStudy(): Unit = runBlocking {//测试用例函数
}
fun main() :Unit= runBlocking{//main函数
}

3、作用域CoroutineScope

是一个接口,没有任何抽象方法需要实现,仅仅维护一个成员变量 CoroutineContext(协程上下文),将作为初始上下文对象传递给被创建的协程,不同的实现类或协程作用域函数本质上的区别是持有的协程上下文不同(配置不同)

public interface CoroutineScope {public val coroutineContext: CoroutineContext
}

4、扩展函数/扩展属性

①、launch、async

public fun CoroutineScope.launch(context: CoroutineContext = EmptyCoroutineContext,//默认空上下文start: CoroutineStart = CoroutineStart.DEFAULT,//默认启动模式DEFAULTblock: suspend CoroutineScope.() -> Unit
): Job {//......
}
public fun <T> CoroutineScope.async(context: CoroutineContext = EmptyCoroutineContext,//默认空上下文start: CoroutineStart = CoroutineStart.DEFAULT,//默认启动模式DEFAULTblock: suspend CoroutineScope.() -> T
): Deferred<T> {//......
}

启动模式可以看这篇文章

launch() 和 async() 是 CoroutineScope 接口的扩展函数,继承了它的 CoroutineContext来自传播其上下文元素和可取消性。

launch() 和 async()都能构建跟协程,但是

async不推荐当成跟协程
原因:一般使用async都是为了拿到返回值,而await又是挂起函数,无法获取返回值就跟launch一样,所以不推荐async当做跟协程

GlobalScope.launch {//核心库launch构建协程 launch { //launch 子协程}async{ //async 子协程}
}MainScope().launch { //工厂函数launch构建协程 launch { //launch 子协程}async{ //async 子协程}
}viewModelScope.launch {//jetpack 支持库viewMode launch构建协程launch { //launch 子协程}async{ //async 子协程}
}//虽然async能构建跟协程,但是不推荐
GlobalScope.async{//核心库async构建协程 launch { //launch 子协程}async{ //async 子协程}
}MainScope().async{ //工厂函数async构建协程 launch { //launch 子协程}async{ //async 子协程}
}viewModelScope.async{//jetpack 支持库viewMode async构建协程launch { //launch 子协程}async{ //async 子协程}
}

②、join、await

示例1:三个协程如下,运行情况

@Test
fun launchStudy(): Unit = runBlocking {val job1 = launch {delay(3000)println("第一个协程执行 success")}val job2 = launch {delay(2000)println("第二个协程执行 success")}val job3 = launch {delay(1000)println("第三个协程执行 success")}
}//执行结果
第三个协程执行 success
第二个协程执行 success
第一个协程执行 success

示例2:如果想让着三个launch协程按顺序执行,就需要用join让程序等待协程执行完成

@Test
fun launchStudy(): Unit = runBlocking {val job1 = launch {delay(3000)println("第一个协程执行 success")}job1.join()val job2 = launch {delay(2000)println("第二个协程执行 success")}job2.join()val job3 = launch {delay(1000)println("第三个协程执行 success")}job3.join()
}
//执行结果
第一个协程执行 success
第二个协程执行 success
第三个协程执行 success

示例3:如果想让着三个async 协程按顺序执行,就需要用await让程序等待协程执行完成,await能拿到协程的返回值

@Test
fun asyncStudy(): Unit = runBlocking {val deferred1 = async {delay(3000)"第一个协程执行 success" //返回值}//deferred1.join() 调用join也是可以的,但是拿不到返回值println(deferred1.await()) //获取返回值val deferred2 = async {delay(2000)"第二个协程执行 success" //返回值}//deferred2.join()println(deferred2.await()) //获取返回值val deferred3 = async {delay(1000)"第三个协程执行 success" //返回值}//deferred3.join()println(deferred3.await()) //获取返回值
}//执行结果
第一个协程执行 success
第二个协程执行 success
第三个协程执行 success

示例4:通常我们不会像示例3一样,因为我们多线程编程就是为了尽量节省时长,正确使用 await 实现多线程并发如下

@Test
fun asyncStudy(): Unit = runBlocking {val deferred1 = async {delay(3000)"第一个协程执行 success"}val deferred2 = async {delay(2000)"第二个协程执行 success"}val deferred3 = async {delay(1000)"第三个协程执行 success"}println("${deferred1.await()},${deferred2.await()},${deferred3.await()}")}//执行结果
第一个协程执行 success,第二个协程执行 success,第三个协程执行 success

③、其他扩展

<1>、cancle()

public fun cancel(cause: CancellationException? = null)

cancle() 函数的作用就是通过构建协程返回的Job、deferred来取消协程

  • 取消父协程,子协程也会跟着取消
  • 取消可以传参数添加一个异常描述,默认为空

示例1:取消单个协程

@Test
fun launchStudy(): Unit = runBlocking {val job1 = launch {println("第一个协程执行 start")delay(2000)println("第一个协程执行 success")}job1.cancel()val job2 = launch {println("第二个协程执行 start")delay(1000)println("第二个协程执行 success")}job2.cancel()
}//执行输出
没有任何输出,调度前协程被取消

示例2:将第二个协程当成第一协程的子协程,取消协程1

@Test
fun launchStudy(): Unit = runBlocking {val job1 = launch {println("第一个协程执行 start")delay(2000)println("第一个协程执行 success")val job2 = launch {println("第二个协程执行 start")delay(1000)println("第二个协程执行 success")}}job1.cancel()
}//执行输出
没有任何输出,调度前协程被取消

示例3:主动取消协程,定义取消描述

@Test
fun launchStudy(): Unit = runBlocking {val job1 = launch {try {println("第一个协程执行 start")delay(2000)println("第一个协程执行 success")} catch (e: CancellationException) {println("协程取消 $e")}}delay(1)//让协程进入执行阶段job1.cancel("我主动取消协程")
}//执行输出
第一个协程执行 start
协程取消 java.util.concurrent.CancellationException: 我主动取消协程

<2>、ensureActive()、isActive

扩展函数 ensureActive:ensureAlive 实际上是对 isActive 封装

public fun CoroutineScope.ensureActive(): Unit = coroutineContext.ensureActive()//ensureAlive 实际上是对 isActive 封装
public fun Job.ensureActive(): Unit {if (!isActive) throw getCancellationException()
}

扩展属性 isActive

public val CoroutineScope.isActive: Booleanget() = coroutineContext[Job]?.isActive ?: true

如果协程进入CPU密集型计算的场景,这个时候调用cancle是无效的,因为这个时候线程的执行权已经被CPU密集型计算占满了,所以根本没有资源给你执行cancle指令,这个时候就需要用到 ensureAlive()、isAlive 来检测协程生命周期状态

示例1:模拟CPU密集型计算,执行下面代码程序会卡死~~

@Test
fun coroutineStudy(): Unit = runBlocking {var item = 0val job = launch(Dispatchers.Default) {println("launch is start")val currentTimeMillis = System.currentTimeMillis()while (item < Int.MAX_VALUE) {if (System.currentTimeMillis() > currentTimeMillis) {println("current item=${item++}")}}}delay(1)//让协程进入执行阶段//调度前取消job.cancel()
}

示例2:解决方案1使用 isAlive
结果:执行一段时间 协程取消

@Test
fun coroutineStudy(): Unit = runBlocking {var item = 0val job = launch(Dispatchers.Default) {println("launch is start")val currentTimeMillis = System.currentTimeMillis()while (item < Int.MAX_VALUE && isActive) {//每次循环检测一下状态if (System.currentTimeMillis() > currentTimeMillis) {println("current item=${item++}")}}}delay(1)//调度前取消job.cancel()}

示例3:解决方案2使用 ensureActive
结果:执行一段时间 协程取消

@Test
fun coroutineStudy2(): Unit = runBlocking {var item = 0val job = launch(Dispatchers.Default) {println("launch is start")val currentTimeMillis = System.currentTimeMillis()while (item < Int.MAX_VALUE) {ensureActive()if (System.currentTimeMillis() > currentTimeMillis) {println("current item=${item++}")}}}delay(1)//调度前取消job.cancel()}

<3>、plus()

用来将指定的上下文元素合并(已存在同类型会覆盖)

public operator fun CoroutineScope.plus(context: CoroutineContext): CoroutineScope =ContextScope(coroutineContext + context)

5、实现类

GlobalScope、MainScope、lifecycleScope、viewModelScope构建协程用法都是相同的,一般用来构建一个根协程。

作用域 说明 建议
GlobalScope 全局作用域,跟随整个进程的生命周期 不推荐使用
MainScope 跟Activity/Fragment生命周期挂钩 推荐使用
LifecycleOwner.lifecycleScope 跟Lifecycle生命周期挂钩,由jetpack提供 推荐使用
ViewModel.viewModelScope 跟ViewModel生命周期挂钩,由jetpack提供 推荐使用

①、核心库

GlobalScope:单例对象,不推荐使用。全局协程作用域,不绑定到任何Job上无法取消,通过它启动的子协程不会阻塞其所在线程可以一直运行到APP停止,子协程运行在自己的调度器上不会继承上下文与父协程没有联系,因此所有开启的协程都需要分别手动来管理。

public object GlobalScope : CoroutineScope {//...
}

ContextScope:上下文作用域,根据指定的上下文创建协程作用域。使用工厂函数 MainScope()、CoroutineScope() 传入上下文对象参数,获取到的就是 ContextScope 实例。

internal class ContextScope(context: CoroutineContext) : CoroutineScope {//...
}

②、平台支持

ViewModel.viewModelScope:由jetpack提供,在 ViewModel 销毁时协程作用域会自动被 cancel,避免造成协程泄漏(内存泄漏)。

public val ViewModel.viewModelScope: CoroutineScope

LifecycleOwner.lifecycleScope :由jetpack提供,在 Activity/Fragment 中使用,UI 销毁时协程作用域会自动被 cancel,避免造成协程泄漏(内存泄漏)。

public val LifecycleOwner.lifecycleScope: LifecycleCoroutineScope

③、工厂函数

MainScope:该调度程序会绑定到主线程,在 onDestroy() 中调用 cancel() 关闭协程。可用于主动控制协程的生命周期,对Android开发意义在于避免内存泄漏。

public fun MainScope(): CoroutineScope = ContextScope(SupervisorJob() + Dispatchers.Main)

示例:

class StudyActivity : AppCompatActivity() {private val mainScope by lazy {MainScope()}override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_test)mainScope.launch { println("mainScope is start")}}override fun onDestroy() {super.onDestroy()mainScope.cancel()}
}

CoroutineScope:根据自定义上下文创建协程作用域,不包含Job则自动创建以。CoroutineScope是一个只包含 coroutineContext 属性的接口,虽然我们可以创建一个实现类但这不是一个流行的做法,而且存在不小心在其它地方取消作用域。通常我们会更喜欢通过对象来启动协程,最简单的办法是使用 CoroutineScope() 工厂函数,它用传入的上下文来创建作用域(如果上下文中没有 Job 会自动创建一个用于结构化并发)。

public fun CoroutineScope(context: CoroutineContext): CoroutineScope =ContextScope(if (context[Job] != null) context else context + Job())
@Test
fun coroutineTest(): Unit= runBlocking {//CoroutineScope 是工厂模式创建的val coroutineScope = CoroutineScope(Job()+ Dispatchers.Default)//自定义的作用域println("调度前。。。。")coroutineScope.launch {println("launch1 start")delay(1000)println("launch1 end")}coroutineScope.launch {println("launch2 start")delay(1000)println("launch2 end")}delay(500)coroutineScope.cancel()//这样创建协程的意义就是,让结构化并发更具有稳定性,可以同时控制取消
}//执行输出
调度前。。。。
launch1 start
launch2 start

6、协程作用域函数


都是挂起函数不会阻塞线程。由于挂起需要协程环境,只能由其它挂起函数或协程调用,因此只能用来创建子协程。

①、coroutineScope( )

public suspend fun <R> coroutineScope(block: suspend CoroutineScope.() -> R): R {//...
}

使用场景:经常被用来包装一个挂起函数的主体,多用于并行分解任务。

示例:协程3取消后,所有协程都取消了

@Test
fun coroutineScopeTest(): Unit = runBlocking {//一个子协程出现问题,其他兄弟协程全部取消coroutineScope {launch {println("launch 1 start")delay(1000)println("launch 1 end")}launch {println("launch 2 start")delay(1000)println("launch 2 end")}launch {println("launch 3 start")throw NullPointerException()delay(1000)println("launch 3 end")}}
}//执行输出
launch 1 start
launch 2 start
launch 3 startjava.lang.NullPointerExceptionat com.example.mycoroutine.Simple17$coroutineScopeTest$1$1$3.invokeSuspend(Simple17.kt:30)at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)

②、supervisorScope( )

public suspend fun <R> supervisorScope(block: suspend CoroutineScope.() -> R): R {//...
}

使用场景:主要用于启动多个独立任务。

示例:协程3取消后,其他兄弟协程正常运行

@Test
fun supervisorScopeTest(): Unit = runBlocking {//一个子协程出现问题,其他兄弟协程正常运行supervisorScope {launch {println("launch 1 start")delay(1000)println("launch 1 end")}launch {println("launch 2 start")delay(1000)println("launch 2 end")}launch {println("launch 3 start")throw NullPointerException()delay(1000)println("launch 3 end")}}
}//执行输出
launch 1 start
launch 2 start
launch 3 start
Exception in thread "Test worker @coroutine#4" java.lang.NullPointerExceptionat com.example.mycoroutine.Simple17$supervisorScopeTest$1$1$3.invokeSuspend(Simple17.kt:58)at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)at kotlinx.coroutines.EventLoopImplBase.processNextEvent(EventLoop.common.kt:274)launch 1 end
launch 2 end

③、withContext( )

public suspend fun <T> withContext(context: CoroutineContext,block: suspend CoroutineScope.() -> T): T {//...
}

使用场景:经常用来指定协程执行的线程和启动模式

示例:通过withContext切换执行的线程

@Test
fun coroutineStudy4(): Unit = runBlocking {println("start "+Thread.currentThread().name)withContext(Dispatchers.IO) {//切换线程到IO线程运行println("withContext "+Thread.currentThread().name)}
}//执行输出
start Test worker @coroutine#1
withContext DefaultDispatcher-worker-1 @coroutine#1

④、withTimeout( )

public suspend fun <T> withTimeout(timeMillis: Long, block: suspend CoroutineScope.() -> T): T {//...
}

使用场景:超时未执行完会抛异常,并返回一个值。超时抛出的TimeoutCancellationException

示例:6s超时协程,到时直接报错

@Test
fun coroutineStudy(): Unit = runBlocking {withTimeout(6000) {for (i in 1..Int.MAX_VALUE) {delay(1000)println("item i=$i")}}
}//执行输出
item i=1
item i=2
item i=3
item i=4
item i=5Timed out waiting for 6000 ms
kotlinx.coroutines.TimeoutCancellationException: Timed out waiting for 6000 ms(Coroutine boundary)at com.example.mycoroutine.Simple28$coroutineStudy$1$1.invokeSuspend(Simple28.kt:16)at com.example.mycoroutine.Simple28$coroutineStudy$1.invokeSuspend(Simple28.kt:14)
Caused by: kotlinx.coroutines.TimeoutCancellationException: Timed out waiting for 6000 ms

⑤、withTimeoutOrNull( )

public suspend fun <T> withTimeoutOrNull(timeMillis: Long, block: suspend CoroutineScope.() -> T): T? {//...
}

使用场景:超时未执行完不抛异常,返回null。如网络请求超时

示例:6s超时后返回了 null

@Test
fun coroutineStudy3(): Unit = runBlocking {val withTimeoutOrNull = withTimeoutOrNull(6000) {for (i in 1..Int.MAX_VALUE) {delay(1000)println("item i=$i")}"执行完成"}println(withTimeoutOrNull ?: "执行超时")
}//执行输出
item i=1
item i=2
item i=3
item i=4
item i=5
执行超时

7、创建协程的区别

协程构建器和协程作用域函数中都包含了形参 block: suspend CoroutineScope.() -> Unit

Kotlin协程:CoroutineScope 作用域相关推荐

  1. Kotlin协程 - - -启动与取消、作用域

    一.协程的启动 1.协程构建器 launch与async构建器都用来启动新协程: launch,返回一个Job并且不附带任何结果值. async,返回一个Deferred,Deferred也是一个jo ...

  2. 动手实现Kotlin协程同步切换线程,以及Kotlin协程是如何实现线程切换的

    前言 突发奇想想搞一个同步切换线程的Kotlin协程,而不用各种withContext(){},可以减少嵌套且逻辑更清晰,想实现的结果如下图: 分析 实现我们想要的结果,首先需要知道协程为什么可以控制 ...

  3. android 协程,Android 上的 Kotlin 协程

    协程是一种并发设计模式,您可以在 Android 平台上使用它来简化异步执行的代码.协程是在版本 1.3 中添加到 Kotlin 的,它基于来自其他语言的既定概念. 在 Android 上,协程有助于 ...

  4. pdf 深入理解kotlin协程_协程初探

    Hello,各位朋友,小笨鸟我回来了! 近期学习了Kotlin协程相关的知识,感觉这块技术在项目中的可应用性很大,对项目的开发效率和维护成本有较大的提升.于是就考虑深入研究下相关概念和使用方式,并引入 ...

  5. 一文看透 Kotlin 协程本质

    前言 公司开启新项目了,想着准备亮一手 Kotlin 协程应用到项目中去,之前有对 Kotlin 协程的知识进行一定量的学习,以为自己理解协程了,结果--实在拿不出手! 为了更好的加深记忆和理解,更全 ...

  6. Kotlin协程-Coroutines-原汁原味一篇就够了系列

    文章目录 Kotlin协程-Coroutines 1. 协程概述 1.1 来自官方的解释:[Coroutines Guide - Kotlin Programming Language](https: ...

  7. Kotlin协程 - - - 协程的简单使用

    一.协程介绍 1.简介 协程是一种并发设计模式,您可以在 Android 平台上使用它来简化异步执行的代码.协程是在版本 1.3 中添加到 Kotlin 的,它基于来自其他语言的既定概念. 在 And ...

  8. kotlin协程_使Kotlin协程无缝采用的5个技巧

    kotlin协程 After successfully adopting coroutines in my prod project I think it is time to share 5 tip ...

  9. 【Kotlin 协程】协程取消 ② ( CPU 密集型协程任务取消 | 使用 isActive 判定协程状态 | 使用 ensureActive 函数取消协程 | 使用 yield 函数取消协程 )

    文章目录 一.CPU 密集型协程任务取消 二.使用 isActive 判定当前 CPU 密集型协程任务是否取消 三.使用 ensureActive 自动处理协程退出 四.使用 yield 函数检查协程 ...

最新文章

  1. TOJ 3750: 二分查找
  2. PHP的CI框架学习
  3. 由创建一个不能被继承的类引发的对象模型的思考
  4. HDU2102 A计划
  5. 在一个数组中找出和为目标值的那 两个 整数,并返回他们的数组下标python代码(Leetcode1)
  6. MySQL(9)主从复制与读写分离
  7. 锋利的jQuery--编写jQuery插件(读书笔记五)[完结篇]
  8. php实现服务器文件同步,PHPstorm配置同步服务器文件
  9. Lesson 3 上机练习题——继承
  10. 论坛apache伪静态_修正PostViews插件加入CDN静态缓存后计数不正常问题
  11. JAVA代码实现MD5加密算法
  12. mapinfo二次开发之:MapX和MapXtreme区别
  13. 文本蕴涵模型测试过程
  14. 【转载】KAB春纳 | 一同乘风破浪吧
  15. DARTS 可微 架构搜索
  16. android解析xml-豆瓣电影API的xml解析实例
  17. 新版gitbook生成电子书指南
  18. 短时记忆想要变成长时记忆,是需要专门付出努力的,关联越多,越能记住
  19. SpringCloud实现微信消息推送
  20. http的无连接与无状态

热门文章

  1. 高效而优雅的编写jQuery
  2. 英语俚语VS汉语俗语:狗改不了吃屎
  3. 商用密码领域骨干企业格尔软件加入龙蜥社区,共建信息安全底座
  4. 去掉/隐藏Firefox标题栏
  5. 程序员,读研你的优势是什么呢?
  6. IDEA 最新15款插件
  7. Linux修改远程连接端口
  8. PHP 零基础入门笔记(5):PHP 运算符 operator
  9. 关于《越狱》,你不知道的5件事
  10. 《深入学习VMware vSphere 6》——第1章 vSphere虚拟化基础与规划 1.1虚拟化基础概念...