Kotlin 协程基础入门:协程的上下文 Context (万物皆为Context)

其他相关文章
Kotlin 协程基础入门:协程的三种启动方式和挂起函数
Kotlin 协程基础入门:Job和协程的生命周期
Kotlin 协程与 Channel(通道)
Kotlin 协程与 Flow


简介

Kotlin 协程中的 CoroutineContext(协程上下文)是一个包含了各种协程参数和配置信息的类。它是一个键-值对的集合,其中的键由 CoroutineContext.Key 定义,值可以是任意类型的对象。协程上下文可以被认为是一个环境,在协程执行过程中提供各种信息和支持。

CoroutineContext 中的常用键:

  1. Job: 协程的 Job 对象,用于管理协程的生命周期和状态。可以使用 cancel() 函数取消协程。
  2. CoroutineExceptionHandler: 协程的异常处理器,用于捕获协程中抛出的异常。
  3. CoroutineName:协程的名称,用于区分不同协程。
  4. CoroutineScope:协程的作用域,可以通过它启动、取消、管理多个协程。

CoroutineContext 中包含的元素可以是调度器、监视器、异常处理器等,它们共同组成了一个完整的协程上下文。

借用下朱涛大神的图片理解下协程上下文:
在这里插入图片描述

一. 关于CoroutineContext 的一些介绍**

1.获取 CoroutineContext

val context = coroutineContext

可以通过调用 coroutineContext 扩展函数,获取当前协程的 CoroutineContext。

2.使用 CoroutineContext

withContext(Dispatchers.IO + Job()) {
    // 这里是使用了新CoroutineContext的执行代码块
}

使用 withContext() 函数切换了协程的执行上下文,同时将一个新的 Job 对象添加到了当前上下文中。

3.继承 AbstractCoroutineContextElement 抽象类实现自己的 CoroutineContext, 例如CoroutineDispatcher

public abstract class CoroutineDispatcher : AbstractCoroutineContextElement(ContinuationInterceptor), ContinuationInterceptor {}

public interface ContinuationInterceptor : CoroutineContext.Element {}
  • 由此可见,Dispatcher 实际上就是 CoroutineContext。

也就可以理解上面 Dispatchers.IO + Job()用法了,归根到底他们都是同类型的,至于+ 号是kotlin 中的运算符重载 operator

4.协程上下文的继承关系

当启动一个新的协程时,会从当前协程的 CoroutineContext 中派生出一个新的 CoroutineContext,其继承关系与普通的类继承关系类似。新的 CoroutineContext 会继承所有父级 CoroutineContext 中的元素,同时也可以添加、替换或删除元素。

二. 协程上下文中的调度器 DefaultDispatcher

DefaultDispatcher 是 Kotlin 协程中默认的调度器,并且是协程上下文中的一种元素,其作用是协调协程的执行。它提供了通用的线程池来执行并发操作,并保证协程的切换和调度。
协程上下文中的调度器决定了协程的执行运行在哪个线程,使用哪个线程池来管理其任务。因此,调度器对于协程的性能和稳定性具有非常重要的影响,DefaultDispatcher 作为协程上下文中的默认调度器,提供了一个合适的默认配置,可以满足大部分场景的需要。

我们可以通过使用 withContext 函数来创造一个包含 DefaultDispatcher 的协程上下文,如下所示:

    suspend fun exampleFunction() = withContext(Dispatchers.Default) {
        // 协程上下文中的代码执行于 DefaultDispatcher 线程池中
    }

我们在使用withContext指定线程池之后Lambda中的代码就会被分发到 DefaultDispatcher 线程池中去执行,而它外部的所有代码仍然还是运行在 main 之上。

除了Dispatchers.Default,Kotlin中还内置了其他几种 Dispatcher:

  • Dispatchers.Default:在 Kotlin 协程中,如果没有明确指定调度器,那么就会使用 DefaultDispatcher,它采用的是线程池技术,线程数量并不是固定的,而是根据需要动态地进行调整。DefaultDispatcher 适合于 CPU 密集型任务,如果不确定使用哪个调度器,就可以考虑使用默认的 DefaultDispatcher。
  • Dispatchers.Unconfined:代表无所谓,当前协程可能运行在任意线程之上,慎用!!!
  • Dispatchers.IO:IO 调度器适合于 I/O 密集型任务,它会创建一个线程池来执行 I/O 操作,线程池中的线程数量是固定的,一般是大于等于 CPU 核心数量的。具体线程的数量我们可以通过参数来配置:kotlinx.coroutines.io.parallelism。
  • Dispatchers.Main:它只在 UI 编程平台才有意义,在 Android、Swing 之类的平台上,一般只有 Main 线程才能用于 UI 绘制。这个 Dispatcher 在普通的 JVM 工程当中,是无法直接使用的。

三. 协程作用域 CoroutineScope

在 Kotlin 协程中,CoroutineScope 是一个核心概念。它用于管理协程的生命周期、范围和作用域,可以让我们方便地控制协程的启动、取消、异常处理等操作。

//CoroutineScope 源码
public interface CoroutineScope {
    public val coroutineContext: CoroutineContext
}

由上面CoroutineScope 源码可以看出,它只包含唯一的元素CoroutineContext,所以它只是对 CoroutineContext 做了一层封装而已,它的核心能力其实都来自于 CoroutineContext。

下面是一个使用 CoroutineScope 使用并管理协程的简单示例:

class MainActivity : AppCompatActivity() {

    private val coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Main)

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

        coroutineScope.launch {
            try {
                // 并发执行两个网络请求并等待它们的结果
                val data1 = async { getDataFromServer1() }.await()
                val data2 = async { getDataFromServer2() }.await()

                // 将请求结果展示到界面上
                showData(data1, data2)

            } catch (e: Exception) {
                // 处理异常
                showErrorToast()
            }
        }
    }

    private suspend fun getDataFromServer1(): Data {
        // 发起网络请求获取第一个数据
        // ...
    }
    
    private suspend fun getDataFromServer2(): Data {
        // 发起网络请求获取第二个数据
        // ...
    }

    private fun showData(data1: Data, data2: Data) {
        // 将两个请求的结果展示到界面上
        // ...
    }

    private fun showErrorToast() {
        // 显示错误提示
        // ...
    }

    override fun onDestroy() {
        super.onDestroy()
        // 取消所有协程的执行
        coroutineScope.cancel()
    }
}

在上面代码中使用了 SupervisorJob() 来创建了一个具有容错能力的协程范围,该协程范围可以避免一个子协程的异常影响到其他子协程的执行
在 onDestroy 方法中调用了 cancel 方法来取消所有协程的执行。可以避免内存泄漏和其他潜在问题。

  • So 我们知道了 CoroutineScope 最大的作用,就是可以方便我们批量控制协程。

四. 协程上下文中的异常处理 CoroutineExceptionHandler

CoroutineExceptionHandler 是一个异常处理器接口,可以使用它来处理协程中发生的未捕获异常。
CoroutineExceptionHandler 示例代码:

val handler = CoroutineExceptionHandler { _, exception ->
    // 在这里处理异常,例如打印日志或将异常崩溃信息上传到服务器
    Log.e("CoroutineExceptionHandler", "Caught an exception: $exception")
}

GlobalScope.launch(handler) {
    // 在这里执行协程,可以在协程中抛出异常
    throw NullPointerException("Something went wrong!")
}

上面示例为一个全局作用域的异常处理器,除了全局作用域的异常处理器外,我们还可以使用局部作用域的异常处理器。例如:

val handler = CoroutineExceptionHandler { _, exception ->
    // 在这里处理异常,例如打印日志或将异常崩溃信息上传到服务器
    Log.e("CoroutineExceptionHandler", "Caught an exception: $exception")
}

val coroutineScope = CoroutineScope(Dispatchers.Main + handler)

coroutineScope.launch {
    // 在这里执行协程,可以在协程中抛出异常
    throw NullPointerException("Something went wrong!")
}

需要注意的是,异常处理器只能用于处理未捕获的异常,如果在协程内部使用 try-catch 语句捕获了异常,则异常处理器无法捕获到这些异常。

在并发场景下,多个协程可能同时崩溃,导致多个异常同时传播到父协程的异常处理器中,这时需要使用 SupervisorJobsupervisorScope 来处理这种情况。这些方法可以保证当子协程发生异常时,只有它本身会失败,而不会影响其他兄弟协程的执行,确保协程层次结构的稳定性。

协程的内部异常处理结合它结构化并发的特点(子协程崩溃如果当前协程未捕获异常的话会继续向上抛出,直到被捕获或程序崩溃),其实要想彻底搞懂,还是挺困难的,这也是它不被广泛开发者使用和接受的原因之一吧!想进一步了解协程异常处理的机制,如上面提到的 SupervisorJobsupervisorScope 可以单独了解下。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Kotlin程是一种轻量级的线程处理机制,它可以在不创建新线程的情况下实现异步操作。Kotlin程的优势在于它提供了一种结构化并发的方式,使得异步代码更加易于编写和维护。下面是深入理解Kotlin程的一些方法和步骤: 1. 程的基本概念:程是一种轻量级的线程处理机制,它可以在不创建新线程的情况下实现异步操作。程的本质是一种作式的多任务处理机制,它可以在同一个线程中切换执行不同的任务,从而实现异步操作。 2. 程的使用方法:在Kotlin中,程的使用方法非常简单。首先需要导入kotlinx.coroutines库,然后使用launch函数创建一个程。在程中可以使用挂起函数来实现异步操作,例如delay函数可以让程暂停一段时间。 3. 程的挂起函数:程的挂起函数是一种特殊的函数,它可以让程暂停执行,等待某个条件满足后再继续执行。在Kotlin中,常用的挂起函数包括delay函数、withContext函数和async函数等。 4. 程的上下文程的上下文是一种特殊的对象,它包含了程的执行环境和状态信息。在Kotlin中,程的上下文可以通过CoroutineContext对象来表示,它包含了程的调度器、异常处理器和其他一些属性。 5. 程的异常处理:程的异常处理是一种特殊的机制,它可以让程在发生异常时自动恢复或者终止执行。在Kotlin中,程的异常处理可以通过try-catch语句或者CoroutineExceptionHandler对象来实现。 6. 程的取消:程的取消是一种特殊的机制,它可以让程在不需要继续执行时自动终止。在Kotlin中,程的取消可以通过cancel函数或者程作用域来实现。 下面是一个使用Kotlin程实现异步操作的例子: ```kotlin import kotlinx.coroutines.* fun main() = runBlocking { val job = launch { delay(1000L) println("World!") } println("Hello,") job.join() } ``` 输出结果为: ``` Hello, World! ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值