Kotlin协程的学习笔记

1、什么是协程:

协程的理解和线程有点类似,可以简单的理解为轻量级的线程。线程需要依靠操作系统的调度才能实现不同线程之间的转换,而协程却可以的编程语言的层面实现不同协程之间的转换。协程允许我们在单线程模式下模拟多线程的编程效果,代码的挂起和恢复都是由编程语言自行控制。

developers:

协程是一种并发设计模式,您可以在 Android 平台上使用它来简化异步执行的代码。协程是在版本 1.3 中添加到 Kotlin 的,它基于来自其他语言的既定概念。

2、功能:(来自developers)

与其他异步解决方案相比,协程具备诸多优势,其中包括:

  • 轻量:您可以在单个线程上运行多个协程,因为协程支持暂停,不会使正在运行协程的线程阻塞。暂停比阻塞节省内存,且支持多个并行操作。
  • 内存泄露更少:使用结构化并发机制在一个作用域内执行多个操作。
  • 内置取消支持取消功能会自动通过正在运行的协程层次结构传播。
  • Jetpack 集成:许多 Jetpack 库都包含提供全面协程支持的扩展。某些库还提供自己的协程作用域,可供您用于结构化并发。

3、基本用法:

首先添加依赖:

implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0"

 首先我们在函数中直接调用:

fun main() {
    GlobalScope.launch {
        println("codes has start")
    }
}

运行后发现没有输出,这是因为Global.lunch函数每次都是一个顶层协程,当程序运行结束时会跟着一起结束,这样我们在主程序中阻塞一秒:

fun main() {
    GlobalScope.launch {
        println("codes has start")
    }
    Thread.sleep(1000)
}

这样便得到了以下结果

但是,如果代码块中1秒执行不完,依然会有问题,因此引出了runBlocking:

 

fun main() {
    runBlocking {
        println("codes has start")
    }
}

他可以保证协程中的代码全部执行完毕之前一直阻塞当前线程,但是如果是正式环境容易产生性能问题。

随着lunch函数的逻辑越来越复杂,我们会将一部分代码提取到单独的函数中。但是lunch函数编写的代码都是有协程作用域的,但是单独的函数并没有协程作用域,这时需要借助coroutineScope函数来提供协程作用域。而coroutineScope是一个挂起函数,kotlin规定,挂起函数之间是可以相互调用的。

    suspend fun pringDot()= coroutineScope {
        launch {
            println(".")
            delay(1000)
        }
    }

其中:

public suspend fun delay(timeMillis: Long) {
    if (timeMillis <= 0) return // don't delay
    return suspendCancellableCoroutine sc@ { cont: CancellableContinuation<Unit> ->
        cont.context.delay.scheduleResumeAfterDelay(timeMillis, cont)
    }
}

/** Returns [Delay] implementation of the given context */
internal val CoroutineContext.delay: Delay get() = get(ContinuationInterceptor) as? Delay ?: DefaultDelay

可以看到,delay函数是一个挂起函数。

需要说明的是coroutineScope和runblocking十分的相似,coroutineScope会将当前协程阻塞,只有当其作用域的代码全部执行完毕后其之后的代码才能执行。但是和runblocking不同的是,它只会阻塞当前协程,不会影响其他协程,也不会影响任何线程。

4、处理长时间任务:

协程在常规函数的基础上增加了两项操作:

  • suspend 用于暂停执行当前协程,并保存所有局部变量。
  • resume 用于让已暂停的协程从暂停处继续执行。

 如以下就是一个简单的协程实现;

suspend fun fetchDocs() {                             // Dispatchers.Main
    val result = get("https://developer.android.com") // Dispatchers.IO for `get`
    show(result)                                      // Dispatchers.Main
}

suspend fun get(url: String) = withContext(Dispatchers.IO) { /* ... */ }

在上面的示例中,get() 仍在主线程上运行,但它会在启动网络请求之前暂停协程。当网络请求完成时,get 会恢复已暂停的协程,而不是使用回调通知主线程。

协程的调度程序:

  • Dispatchers.Main - 使用此调度程序可在 Android 主线程上运行协程。此调度程序只能用于与界面交互和执行快速工作。示例包括调用 suspend 函数、运行 Android 界面框架操作,以及更新 LiveData 对象。
  • Dispatchers.IO - 此调度程序经过了专门优化,适合在主线程之外执行磁盘或网络 I/O。示例包括使用 Room 组件、从文件中读取数据或向文件中写入数据,以及运行任何网络操作。
  • Dispatchers.Default - 此调度程序经过了专门优化,适合在主线程之外执行占用大量 CPU 资源的工作。用例示例包括对列表排序和解析 JSON。

 重要提示:使用 suspend 不会让 Kotlin 在后台线程上运行函数。suspend 函数在主线程上运行是一种正常的现象。在主线程上启动协程的情况也很常见。当您需要确保主线程安全时(例如,从磁盘上读取数据或向磁盘中写入数据、执行网络操作或运行占用大量 CPU 资源的操作时),应始终在 suspend 函数内使用 withContext()

5、启动协程

  • launch 可启动新协程而不将结果返回给调用方。任何被视为“一劳永逸”的工作都可以使用 launch 来启动。
  • async会启动一个新的协程,并允许您使用一个名为 await 的暂停函数返回结果。

 Job 是协程的句柄。使用 launch 或 async 创建的每个协程都会返回一个 Job 实例,该实例唯一标识协程并管理其生命周期。您还可以将 Job 传递给 CoroutineScope 以进一步管理其生命周期,如以下示例所示:

class ExampleClass {
    ...
    fun exampleMethod() {
        // Handle to the coroutine, you can control its lifecycle
        val job = scope.launch {
            // New coroutine
        }

        if (...) {
            // Cancel the coroutine started above, this doesn't affect the scope
            // this coroutine was launched in
            job.cancel()
        }
    }
}

关于withContext()函数,可以理解为async的简化版写法,但是withContext()强制我们指定一个线程参数。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值