返回

协程搬运工 – 上下文和调度器

Android

协程既是一种过程的抽象也是一种调度工具,协程将副程序包装成一个单元,可以在并发环境中执行。协程作为一种调度工具,可以更好地管理协程的生命周期并能够从多线程中解放出来,以便专心编排和协调协程的执行。

在 Kotlin 中,协程运行的上下文是由 CoroutineContext 类表示的,这个上下文包含了一个协程调度器(CoroutineDispatcher),这个调度器决定了相关的协程将要在哪个线程中执行。

当然我们可以按照普通的做法,直接使用协程调度器来启动一个新的协程。如果调度器不支持并发,则需要显式的创建和使用并发调度器。可以从 Dispatchers 工具类中获取 Dispatchers.Default 或 Dispatchers.IO 或 Dispatchers.Unconfined 这些调度器。比如,要创建一个新的协程,并且希望它在后台线程中执行,可以使用以下代码:

val backgroundScope = CoroutineScope(Dispatchers.Default)
backgroundScope.launch {
    // 代码将会在后台线程中执行
}

当然协程也可以创建一个协程上下文,并将调度器作为参数传递,来创建一个新的协程,这个上下文甚至可以包含其他内容,例如 Job 对象。如下代码中创建一个上下文,其中的调度器为 Dispatchers.Unconfined,这个调度器不会把协程限制在任何特定的线程中:

val unconfinedScope = CoroutineScope(CoroutineContext(Dispatchers.Unconfined))
unconfinedScope.launch {
    // 代码将会在任意线程中执行
}

当然,也可以使用 CoroutineScope 来创建一个新的协程,使用 withContext 可以在新的协程中访问旧的协程上下文。如下代码中的协程将使用 Dispatchers.Default 调度器运行:

withContext(Dispatchers.Default) {
    // 代码将会在后台线程中执行
}

协程还提供了一种机制来协调不同协程之间的执行,称为挂起函数。挂起函数是在协程中可以暂停自身执行的函数,允许其他协程执行。 挂起函数可以被其他的协程唤醒,当协程从挂起中恢复时,它会从它暂停的地方继续执行。

例如,假设我们有一个协程,它需要从网络上下载一个文件。我们可以使用以下代码来实现:

suspend fun downloadFile(url: String): ByteArray {
    val client = OkHttpClient()
    val request = Request.Builder()
        .url(url)
        .build()

    val response = client.newCall(request).execute()
    return response.body()?.bytes() ?: ByteArray(0)
}

在这个示例中,downloadFile 函数是一个挂起函数,它使用 suspend 进行声明。这意味着它可以在协程中调用,并且可以在等待网络请求时暂停执行。

如果我们想要在一个协程中下载一个文件,我们可以使用以下代码:

launch {
    val bytes = downloadFile("https://example.com/file.zip")
    // 在这里我们可以使用下载的文件
}

在这个示例中,launch 函数是一个协程构建器函数,它创建一个新的协程并启动它。协程将调用 downloadFile 函数,该函数将暂停执行,直到网络请求完成。当网络请求完成时,协程将恢复执行,并将下载的文件保存在 bytes 变量中。

协程为我们在并发编程中提供了一种新的选择,它可以简化并发编程的复杂性,使开发人员能够编写出更易于理解和维护的代码。