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

Posted 韩曙亮

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Kotlin 协程协程上下文 ( 协程上下文构成要素 | 指定协程上下文元素组合 | 协程上下文元素的继承关系 | 协程上下文元素的几种指定形式 | 默认 | 继承 | 自定义指定 )相关的知识,希望对你有一定的参考价值。

文章目录





一、协程上下文构成要素



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

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

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

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




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



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

/**
 * 返回一个包含来自此上下文和来自其他[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)
                
            
        

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


代码示例 :

// 将主线程包装成协程
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
父类 ;


代码示例 :

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 ;

00:05:32.391  I  StandaloneCoroutineActive@f30fe8 : DefaultDispatcher-worker-1
00:05:32.393  I  StandaloneCoroutineActive@bc6a601 : DefaultDispatcher-worker-2





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



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

  • ① 默认的 协程上下文 CoroutineContext : 下面代码中 launch 构建的协程就是默认参数 ;
    • 默认 协程调度器 CoroutineDispatcher : Dispatchers.Default ;
    • 默认 协程名称 CoroutineName : " coroutine " ;
// 将主线程包装成协程
runBlocking<Unit>
    launch()
        Log.i(TAG, "当前运行的线程 : $Thread.currentThread().name")
    

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


完整代码示例 :

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 的父类协程
        
    

执行结果 :

00:34:00.217  I  DefaultDispatcher-worker-1
00:34:00.217  I  DefaultDispatcher-worker-3

以上是关于Kotlin 协程协程上下文 ( 协程上下文构成要素 | 指定协程上下文元素组合 | 协程上下文元素的继承关系 | 协程上下文元素的几种指定形式 | 默认 | 继承 | 自定义指定 )的主要内容,如果未能解决你的问题,请参考以下文章

Kotlin 协程协程异常处理 ③ ( 协程异常处理器 CoroutineExceptionHandler 捕获异常 | 验证 CoroutineScope 协程的异常捕捉示例 )

Kotlin 协程协程异常处理 ③ ( 协程异常处理器 CoroutineExceptionHandler 捕获异常 | 验证 CoroutineScope 协程的异常捕捉示例 )

深入理解Kotlin协程协程的上下文 CoroutineContext

Kotlin回顾9.协程思维模型

Kotlin 协程协程底层实现 ① ( Kotlin 协程分层架构 | 基础设施层 | 业务框架层 | 使用 Kotlin 协程基础设施层标准库 Api 实现协程 )

Kotlin 协程协程底层实现 ① ( Kotlin 协程分层架构 | 基础设施层 | 业务框架层 | 使用 Kotlin 协程基础设施层标准库 Api 实现协程 )