笔记 | 初探Kotlin协程

2023-03-05 16:34:49 浏览数 (1)

warning: 这篇文章距离上次修改已过188天,其中的内容可能已经有所变动。

如何理解协程

协程是一种不同于进程线程的存在,其本质是一种函数,同一线程中的多个协程是串行执行的,但为了理解仍然需要三者一起对比。

进程、线程、协程三者的上下文切换对比:

进程

线程

协程

切换者

操作系统

操作系统

用户(编程者/应用程序)

切换时机

根据操作系统自己的切换策略,用户不感知

根据操作系统自己的切换策略,用户不感知

用户自己(的程序)决定

切换内容

页全局目录&内核栈&硬件上下文

内核栈&硬件上下文

硬件上下文

切换内容的保存

保存于内核栈中

保存于内核栈中

保存于用户自己的变量(用户栈或者堆)

切换过程

用户态 - 内核态 - 用户态

用户态 - 内核态 - 用户态

用户态(没有陷入内核态)

切换效率

从以上协程的特性中,可以知道协程的应用场景是I/O密集型任务,而非计算密集型任务

info: 我认为关于协程的全部,最佳参考为Google官方编写的文章: 谷歌开发者:在 Android 开发中使用协程 | 背景介绍

在安卓开发中使用协程

Kotlin 1.3版本中开始引入了一种全新处理并发的方式:协程,可以在 Android 平台上使用它来简化异步执行的代码,主要用来解决两个问题:

  1. 处理耗时任务 (Long running tasks),这种任务常常会阻塞住主线程;
  2. 保证主线程安全 (Main-safety) ,即确保安全地从主线程调用任何 suspend 函数。

在 Kotlin 中,所有协程都必须在调度器中运行,即使它们是在主线程上运行也是如此。suspend并不代表后台执行,在哪里执行由调度器决定。协程可以自行暂停,而调度器负责将其恢复。Kotlin提供了以下三种调度器并罗列了其使用场景:

调度器名称

运行线程

使用场景

适用案例1

适用案例2

适用案例3

Dispatchers.Main

Android上的主线程

用来处理UI交互和一些轻量级任务

调用suspend函数

调用UI函数

更新LiveData

Dispatchers.IO

非主线程

专为磁盘和网络IO进行了优化

数据库

文件读写

网络处理

Dispatchers.Default

非主线程

专为CPU密集型任务进行了优化

数组排序

JSON数据解析

处理差异判断

下面是一个使用调度器的例子。在 get 方法的主体内,调用 withContext(Dispatchers.IO) 来创建一个在 IO 线程池中运行的块。放在该块内的任何代码都始终通过 IO 调度器执行。由于 withContext本身就是一个suspend函数,它会使用协程来保证主线程安全。

代码语言:javascript复制
// Dispatchers.Main
suspend fun fetchDocs() {    
    // Dispatchers.Main    
    val result = get("developer.android.google.cn")    
    // Dispatchers.Main    
    show(result)
}
// Dispatchers.Main
suspend fun get(url: String) = withContext(Dispatchers.IO) {        
     // Dispatchers.IO    
}    
// Dispatchers.Main

为了更好地管理和使用协程,一般要指定协程上下文(CoroutineContext)和协程作用域(CoroutineScope)。 CoroutineScope 会跟踪它使用 launchasync 创建的所有协程。您可以随时调用 scope.cancel() 以取消正在进行的工作(即正在运行的协程)。在 Android 中,某些 KTX 库为某些生命周期类提供自己的 CoroutineScope。例如,ViewModelviewModelScopeLifecyclelifecycleScope。不过,与调度程序不同,CoroutineScope 不运行协程。

代码语言:javascript复制
class ExampleClass {

    //异常处理
    private val handler = CoroutineExceptionHandler { _, exception ->
        println("------------BaseViewModel异常捕获--------------")
        exception.printStackTrace()
    }
    protected val parentJob = SupervisorJob()   handler
    
    //协程上下文
    protected open val coroutineContext: CoroutineContext
        get() = parentJob   Dispatchers.IO
    //协程作用域
    protected val scope = CoroutineScope(coroutineContext)
    
    fun exampleMethod() {
        // Starts a new coroutine within the scope
        scope.launch {
            // New coroutine that can call suspend functions
            fetchDocs()
        }
    }

    fun cleanUp() {
        // Cancel the scope to cancel ongoing coroutines work
        scope.cancel()
    }
}

0 人点赞