[Android] kotlin 协程的个人理解

Kotlin协程是Kotlin语言中的一种非阻塞的、顺序执行的编程构造,可以在不阻塞线程的情况下执行异步任务。它与Java中的线程和JavaScript中的Promise类似,但是更轻量级,并且可以更简单地管理异步操作。

Kotlin 协程是一种在Kotlin中处理异步编程和并发任务的强大工具。它们提供了一种轻量级的线程管理方式,允许开发者以同步编程的直观性来写异步代码。协程的构成可以从以下几个关键部分来理解:

1. 协程构建器

  • 协程构建器是启动协程的函数。Kotlin提供了几个这样的构建器,最常用的包括:
    launch:在一个CoroutineScope中启动一个新的协程,不直接返回结果,通常用于执行不返回值的任务。
  • async:类似launch,但它返回一个Deferred对象,可以用来获取协程计算后的结果。
    runBlocking:在当前线程启动一个新的协程,并阻塞当前线程直到协程完成。它主要用于测试和桥接到非协程代码。
  • withContext:允许协程在指定的CoroutineContext中执行,并在同一个协程中切换上下文。

2. 协程上下文 (CoroutineContext)

  • 协程上下文是一组相关属性的集合,包括:
  • Job:控制协程的生命周期,提供取消和完成的能力。这是一个用于管理协程生命周期的元素。Job 支持启动、取消、完成和联结协程。当你取消一个 Job 时,它会取消其所有子协程。SupervisorJob 是一种特殊的 Job,它的一个特点是当一个子协程失败时它不会导致其它子协程被取消。
  • Dispatcher:决定协程中代码运行的线程或线程池。有几个标准的调度器:Dispatchers.Main、Dispatchers.IO、Dispatchers.Default 和 Dispatchers.Unconfined。
  • CoroutineName:为协程提供名称,对于调试非常有用
  • CoroutineExceptionHandler:用于协程内处理未捕获的异常

2.1. 操作

CoroutineContext 的元素可以使用 + 操作符来组合:

val myContext = coroutineContext + Job() + CoroutineName("myCoroutine") + CoroutineExceptionHandler { _, throwable ->
    println("Caught $throwable")
}

2.2. 获取和修改 CoroutineContext

可以通过在协程内部使用 coroutineContext 属性来访问当前协程的上下文。如果你需要在协程上下文中更改或访问特定的元素,可以使用 + 操作符来添加或修改元素,使用 get 函数通过元素的键来访问它们。

import kotlinx.coroutines.*

fun main() = runBlocking<Unit> {
    // 创建一个具有特定名字和异常处理器的上下文
    val myContext = coroutineContext + CoroutineName("myCoroutine") + CoroutineExceptionHandler { _, throwable ->
        println("Caught $throwable")
    }

    // 使用自定义上下文启动一个协程
    val job = launch(myContext) {
        println("Running in coroutine with context: $coroutineContext")
        throw IllegalArgumentException() // 将会被我们的异常处理器捕获
    }
    job.join()
    println("Finished")
}

3. 协程作用域 (CoroutineScope)

每个协程都在CoroutineScope中运行。协程作用域管理协程的生命周期,可以联结协程的结构化并发,例如当一个作用域被取消时,它下面的所有协程都会被取消。

通过GlobalScope可以启动全局协程,但这通常不推荐,因为这种协程的生命周期会与整个应用的生命周期相同,可能会导致内存泄露。

3.1. 结构化并发

结构化并发是Kotlin协程的一个重要原则。它确保协程的生命周期与其所属作用域的生命周期关联。这意味着一个作用域中的协程可以被安全地取消,并且当作用域退出时,启动的所有协程都会自动取消。

3.2. 协程是一种并发设计模式,其特点是:

  • 依赖于线程
  • 挂起时不需要阻塞线程,所以几乎是无代价的。
  • 同一个线程中可以创建多个不同的协程。

3.3. 常用的作用域

  • GlobalScope:全局顶级协程作用域,例如,可以在程序中以如下方式异步执行花括号中的代码
  • MainScope:主线程作用域,全局范围
  • lifecycleScope:生命周期作用域,用于activity、Fragment等有生命周期的组件,在组件DESCROTY后退出。

3.4. 创建作用域

可以通过定义一个CoroutineScope实例来创建一个作用域:

val myScope = CoroutineScope(Dispatchers.Default + Job())

这里创建的myScope拥有Dispatchers.Default的调度器和一个新的Job实例。

当你需要一个与生命周期组件相关联的作用域时(如在UI控制器,例如Android的Activity或Fragment中),应当使用继承或者委托来创建协程作用域:

class MyActivity : AppCompatActivity(), CoroutineScope by CoroutineScope(Dispatchers.Main + SupervisorJob()) {

    override fun onDestroy() {
        super.onDestroy()
        coroutineContext.cancel() // 取消作用域内的所有协程
    }

    fun performAsyncOperation() {
        launch { // 使用扩展函数在Activity的协程作用域内启动协程
            // ...
        }
    }
}

3.5. rememberCoroutineScope()

在Jetpack Compose中,rememberCoroutineScope()是一个特殊的记忆函数,它可以帮助你在Compose函数体内获得一个与Composition生命周期绑定的协程作用域。你可以在这个作用域内启动协程,而这些协程将与你的Composable保持同步,即在Composable处于活跃状态时协程被保留,在Composable离开Composition时协程被取消。

使用rememberCoroutineScope()时,通常是为了实现UI事件触发的异步操作,这包括触发网络请求,数据库读写或者长时间运行的计算等。

需要注意的是,虽然rememberCoroutineScope()提供了一个与Composable相绑的协程作用域,但这个协程作用域通常并不指定任何特定的CoroutineDispatcher。如果你需要在特定线程上执行协程代码(例如,更新UI通常需要在主线程上执行),你应该在协程中明确指定所需的Dispatcher

3.6. LaunchedEffect

如需从可组合项内安全调用挂起函数,请使用 LaunchedEffect 可组合项。当 LaunchedEffect 进入组合时,它会启动一个协程,并将代码块作为参数传递。如果 LaunchedEffect 退出组合,协程将取消。

如果使用不同的键重组 LaunchedEffect(请参阅下方的重启效应部分),系统将取消现有协程,并在新的协程中启动新的挂起函数。所以当touchTime发送改变时候,就会取消旧的协程,并重新delay(5000)

4. suspend 关键字

声明一个函数为挂起函数必须使用suspend关键字。挂起函数可以在不阻塞线程的情况下被挂起(暂停)和恢复(继续执行)。协程中的大多数挂起操作都是通过挂起函数完成的。

suspend就是挂起的意思,其本质就是暂停当前任务,转头去做其它事情,做完以后再回来继续干活。

挂起函数“挂起恢复”的特性只能在协程环境下实现,因此每一个被suspend修饰的方法都必须在另一个suspend函数或者Coroutine协程中进行调用。

suspend涉及到一种被称为CPS(Continuation-Passing-Style)的机制。每一个被suspend修饰的方法或者lambda表达式,都会被编译器额外添加上一个Continuation类型的参数。

@GET("/v2/news")
suspend fun newsGet(@QueryMap params: Map<String, String>): NewsResponse

上面这段代码在经过CPS转换之后,变成:

@GET("/v2/news")
fun newsGet(@QueryMap params: Map<String, String>, c: Continuation<NewsResponse>): Any?

5. 结构化并发

结构化并发是指协程的启动和取消是结构化的,每个协程都与一个父协程关联。当父协程被取消或完成时,所有的子协程也将自动被取消。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值