前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【Kotlin 协程】协程上下文 ( 协程上下文构成要素 | 指定协程上下文元素组合 | 协程上下文元素的继承关系 | 协程上下文元素的几种指定形式 | 默认 | 继承 | 自定义指定 )

【Kotlin 协程】协程上下文 ( 协程上下文构成要素 | 指定协程上下文元素组合 | 协程上下文元素的继承关系 | 协程上下文元素的几种指定形式 | 默认 | 继承 | 自定义指定 )

作者头像
韩曙亮
发布2023-03-30 18:24:50
3720
发布2023-03-30 18:24:50
举报
文章被收录于专栏:韩曙亮的移动开发专栏

文章目录

一、协程上下文构成要素


使用 launch 或 async 协程构建器 启动 协程时 , 都要 指定一个 协程上下文 , 如果没有指定 , 则使用默认的 空的协程上下文 EmptyCoroutineContext ;

  • 下面是 launch 协程构建器的原型 : 第一个参数 协程上下文 CoroutineContext 默认为 EmptyCoroutineContext ;
代码语言:javascript
复制
public fun CoroutineScope.launch(
    context: CoroutineContext = EmptyCoroutineContext,
    start: CoroutineStart = CoroutineStart.DEFAULT,
    block: suspend CoroutineScope.() -> Unit
): Job 
  • 下面是 async 协程构建器的原型 : 第一个参数 协程上下文 CoroutineContext 默认为 EmptyCoroutineContext ;
代码语言:javascript
复制
public fun <T> CoroutineScope.async(
    context: CoroutineContext = EmptyCoroutineContext,
    start: CoroutineStart = CoroutineStart.DEFAULT,
    block: suspend CoroutineScope.() -> T
): Deferred<T> 

协程上下文 CoroutineContext : 是用于定义 协程行为 的一组数据 , 其包含了如下内容 :

  • 协程任务 Job : 用于 控制协程生命周期 ;
  • 协程调度器 CoroutineDispatcher : 用于 分发协程任务 , 被调度主体是 线程 , 也就是安排哪个线程执行哪个任务 ;
  • 协程名称 CoroutineName : 在调试协程程序时 , 可以通过协程名称 分辨协程 ;
  • 协程异常处理器 CoroutineExceptionHandler : 用于处理协程中 未被捕获的异常 ;

二、指定协程上下文元素组合


协程上下文 CoroutineContext 类 , 进行了运算符重载 , 如下为重载内容 :

代码语言:javascript
复制
/**
 * 返回一个包含来自此上下文和来自其他[context]的元素的上下文。
 * 该上下文中与另一个上下文中具有相同键的元素将被删除。
 */
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)
                }
            }
        }

因此 , 可以使用 + 运算符拼装协程 ;

代码示例 :

代码语言:javascript
复制
// 将主线程包装成协程
runBlocking<Unit>{
    launch(
        // 为 协程上下文 指定 协程调度器 + 协程名称 两个元素
        Dispatchers.Default + CoroutineName("Hello")
    ) {
        Log.i(TAG, "当前运行的线程 : ${Thread.currentThread().name}")
    }
}

使用 + 运算符 , 为协程上下文 CoroutineContext 指定

  • 协程调度器 Dispatchers.Default
  • 协程名称 CoroutineName("Hello")

三、协程上下文元素的继承关系


协程上下文元素的继承 : 在 线程 / 协程 中 可以 创建协程 , 创建协程时 , 需要设置 协程上下文 CoroutineContext , 在协程上下文 中 不同元素 有不同的 继承形式 ;

  • 协程任务 Job , 是全新的 ;
  • 协程调度器 CoroutineDispatcher | 协程名称 CoroutineName | 协程异常处理器 CoroutineExceptionHandler 三个元素会从 协程上下文 CoroutineContext 父类 继承 ;

协程上下文 CoroutineContext 父类 , 示例 :

在 协程 A 中 创建 协程 B , 则 协程 A 的 协程上下文 CoroutineContext 就是 协程 B 的 协程上下文 CoroutineContext 的 父类 ;

代码示例 :

代码语言:javascript
复制
package kim.hsl.coroutine

import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import kotlinx.coroutines.*

class MainActivity : AppCompatActivity(){
    val TAG = "MainActivity"

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // 将主线程包装成协程
        runBlocking<Unit>{
            // 创建协程作用域
            // 协程 1
            val coroutineScope = CoroutineScope(Job() + Dispatchers.Default + CoroutineName("Hello"))

            // 协程 2
            val job2 = coroutineScope.launch() {
                // coroutineContext[Job] 可以获取 协程上下文的 Job 元素
                Log.i(TAG, "${coroutineContext[Job]} : ${Thread.currentThread().name}")

                // 协程 3
                val job3 = launch {
                    Log.i(TAG, "${coroutineContext[Job]} : ${Thread.currentThread().name}")
                }
                // 等待 job3 任务执行完毕
                job3.join()
            }

            // 等待 job2 执行完毕
            job2.join()
            
            // 协程 1 是 协程 2 的父类协程
            // 协程 2 是 协程 3 的父类协程
        }
    }
}

执行结果 : 协程任务 Job 是不同的 ; 协程调度器都是 DefaultDispatcher ;

代码语言:javascript
复制
00:05:32.391  I  StandaloneCoroutine{Active}@f30fe8 : DefaultDispatcher-worker-1
00:05:32.393  I  StandaloneCoroutine{Active}@bc6a601 : DefaultDispatcher-worker-2
在这里插入图片描述
在这里插入图片描述

四、协程上下文元素的几种指定形式 ( 默认 | 继承 | 自定义指定 )


协程任务 的 协程上下文元素 由以下几种形式指定 :

  • ① 默认的 协程上下文 CoroutineContext : 下面代码中 launch 构建的协程就是默认参数 ;
    • 默认 协程调度器 CoroutineDispatcher : Dispatchers.Default ;
    • 默认 协程名称 CoroutineName : " coroutine " ;
代码语言:javascript
复制
// 将主线程包装成协程
runBlocking<Unit>{
    launch(){
        Log.i(TAG, "当前运行的线程 : ${Thread.currentThread().name}")
    }
}
  • ② 继承自父类的 协程上下文 CoroutineContext : 继承自 父协程 或 CoroutineScope 的 协程上下文 ; 参考 " 三、协程上下文元素的继承关系 " 中的示例 ;
  • ③ 自定义的 协程上下文 CoroutineContext 元素参数 : 在 协程构建器 中指定的 协程上下文参数 优先级最高 , 可以 覆盖 默认值 和 继承自父类的 协程上下文元素 , 如下代码示例 ;
代码语言:javascript
复制
// 将主线程包装成协程
runBlocking<Unit>{
    launch(
        // 为 协程上下文 指定 协程调度器 + 协程名称 两个元素
        Dispatchers.Default + CoroutineName("Hello")
    ) {
        Log.i(TAG, "当前运行的线程 : ${Thread.currentThread().name}")
    }
}

完整代码示例 :

代码语言:javascript
复制
package kim.hsl.coroutine

import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import kotlinx.coroutines.*

class MainActivity : AppCompatActivity(){
    val TAG = "MainActivity"

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // 将主线程包装成协程
        runBlocking<Unit>{

            // 协程异常处理器
            val coroutineExceptionHandler = CoroutineExceptionHandler {
                    coroutineContext, throwable ->
                Log.i(TAG, "处理协程异常 : ${throwable}")
            }

            // 创建协程作用域
            // 协程 1
            val coroutineScope = CoroutineScope(
                Job() +                         // 协程任务
                        Dispatchers.Main +              // 协程调度器
                        CoroutineName("Hello") +  // 协程名称
                        coroutineExceptionHandler        // 协程异常处理器
            )

            // 协程 2
            // 在 CoroutineScope 中创建 子协程 ,
            // 其协程上下文都继承自 coroutineScope 的协程上下文
            val job2 = coroutineScope.launch(Dispatchers.IO) {
                // 通过线程查看协程调度器
                Log.i(TAG, "${Thread.currentThread().name}")

                // 协程 3
                // 在 job2 协程中创建 子协程 ,
                // 其协程上下文都继承自 job2 的协程上下文
                val job3 = launch() {
                    // 通过线程查看协程调度器 , 该协程的 协程调度器 是 Dispatchers.IO
                    Log.i(TAG, "${Thread.currentThread().name}")
                }
                // 等待 job3 任务执行完毕
                job3.join()
            }

            // 等待 job2 执行完毕
            job2.join()
            
            // 协程 1 是 协程 2 的父类协程
            // 协程 2 是 协程 3 的父类协程
        }
    }
}

执行结果 :

代码语言:javascript
复制
00:34:00.217  I  DefaultDispatcher-worker-1
00:34:00.217  I  DefaultDispatcher-worker-3
在这里插入图片描述
在这里插入图片描述
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-12-03,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 文章目录
  • 一、协程上下文构成要素
  • 二、指定协程上下文元素组合
  • 三、协程上下文元素的继承关系
  • 四、协程上下文元素的几种指定形式 ( 默认 | 继承 | 自定义指定 )
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档