Kotlin协程入门食用指南

简介: 我们需要处理并发过程中的调度问题,使应用程序在这种操作过程中不至于阻塞,而造成应用崩溃。为此,官方推出了协程来编写非阻塞并发代码来解决以上需求,让我们对协程进行相关知识点学习

✨学前温习

线程

线程是执行代码的一种路径,每一行代码对应的一或多条相关指令,指令将会在同一线程上按顺序执行。而在线程的执行中 调度程序会为每一个线程分配一个时间片,线程依据时间片算法进行调度。相关线程要么在该时间片内完成,要么挂起,直到获得另一个时间片。 而在我们的实际项目运行过程中,除了主线程外还有其它线程。处理器将在不同系列的指令之间来回切换,呈现多任务处理的状态。使多个代码乱序执行,或近乎于并发执行,从而更有效地利用资源,操作系统可以根据系统,编程语言和并发单元的特性来管理多任务。如应用界面响应用户操作,应用在后台执行复杂的任务(如网络请求、下载图片),是一种"并发"的概念。

而利用线程简单实现多任务和并发的方式,可能会出现很多问题

由于主线程负责运用应用界面,因此,该线程具有较高的性能,以便应用能够顺畅运行,任何长时间运行的任务在完成之前都会阻塞该线程,从而导致应用无响应。

目前,手机每秒会尝试更新界面60到120次,在60的刷新率下,Android每次刷新界面所需的时间为16ms或者更低,当主线程来不及更新时,Android会中止尝试完成单个更新周期,既丢帧,以试图跟上进度。丢帧和帧数波动属于正常现象。但是丢帧过多会导致应用无响应。

因此,我们需要处理这种并发过程中的调度问题,使应用程序在这种操作过程中不至于阻塞,而造成应用崩溃。

而在多线程中,我们还会遇到竞争,多个线程同时访问内存中的同一个值,从而导致出现脏数据,从而导致应用崩溃。

为此,官方推出了协程的kotlin功能来编写清晰的非阻塞并发代码。

协程作用

  • 处理耗时任务 这种任务常常会阻塞主线程
  • 保证主线程安全 确保安全地从主线程调用任何suspend 函数

AsyncTask 和 协程

但是,我们之前在开发过程中已经学习过了异步,可以利用异步处理耗时操作,再切回主线程执行UI响应。这和我们的协程有什么区别么?

AsyncTask

AsyncTask是异步操作。下面让我们来看一段异步的代码

val submitButton = findViewById<Button>(R.id.bt_submit_main).also {
            it.setOnClickListener {
                object : AsyncTask<Void, Void, Int>(){
                    override fun doInBackground(vararg params: Void?): Int {
                        //耗时操作 
                        return 5;
                    }
                    //回调方法 耗时操作完成后的返回值 
                    override fun onPostExecute(result: Int?) {
                        if (result != null) {
                            nameTv.setText(result)
                        }
                    }
                }.execute()
}

我们可以看到,在利用Async实现异步的过程中,我们在doInBackground内进行了耗时操作,然后我们覆写其onPostExecute回调方法,在回调方法中,获取刚才耗时操作的结果,进行相关处理。 我们再来对比一下协程对于并发过程中的处理操作

GlobalScope.launch(Dispatchers.Main) {
                    withContext(Dispatchers.IO){
                        //耗时操作
                        delay(1200)
                    }
                    //处理结果
                }

我们可以看到,通过协程的处理,我们的程序呈现一种同步代码的感觉,这是协程的一种异步代码同步化,这是一个方面。

另外一个方面,在我们通过async的处理过程中,我们可以看到它的处理过程是通过回调进行结果处理的,而回调,是有可能造成回调地狱的,因此,我们可以尝试使用协程处理这种并发操作。

并且协程可以实现轻量级的并发

实现更高的资源利用率

协程挂起与恢复

常规函数基本操作包含了invoke和return。协程的加入增加了suspend 和 resume

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

第一个协程程序

fun main() {
    GlobalScope.launch(Dispatchers.Main) { // 在后台启动一个新的协程并继续
        delay(1000L) // 非阻塞的等待 1 秒钟(默认时间单位是毫秒)
        println("World!") // 在延迟后打印输出
    }
    println("Hello,") // 协程已在等待时主线程还在继续
    Thread.sleep(2000L) // 阻塞主线程 2 秒钟来保证 JVM 存活
}

代码挂起与阻塞 挂起的作用

上面那段代码,我们可以看到挂起与阻塞

  • 挂起 非阻塞的 当我们在点击事件中执行挂起函数,我们会发现,onclick()事件很快跳出循环,并在阻塞操作完成后继续完成后续操作
  • 阻塞 而阻塞操作 我们在点击事件中执行会造成阻塞的函数,我们会发现,onclick()函数并没有完成,一直等到耗时操作完成后完成后续操作才会onclick()函数。只是一个阻塞过程。当多次在点击事件中执行阻塞函数,当达到一定的耗时时,会造成应用程序无响应,从而造成应用崩溃。

上面我们看到了协程的一部分实现以及挂起的一些操作

而我们的协程实现由两部分构成

  • 基础设施层 标准库的协程API,主要对协程提供了概念和语义上最基本的支持
  • 业务框架层 协程的上传框架支持

我们刚才用的都是业务框架层实现,而在基础设施层对协程的使用,是比较复杂的,下面让我们看看具体的代码实现

//这只是创建了一个协程
                val continuation =suspend {
                    //协程体
                    5 
                }.createCoroutine(object : Continuation<Int>{
                    //CoroutineContext 协程上下文
                    override val context: CoroutineContext
                        get() = EmptyCoroutineContext;
                    //把协程体的执行结果返回 这是一个回调 实际上生成的代码有回调
                    override fun resumeWith(result: Result<Int>) {
                        println(result)
                    }
                })
                //该实例对象保存了挂起点信息 需要利用resume启动协程
                continuation.resume(Unit)

基础设施层中我们调用的是kotlin

业务框架层中我们调用的是kotlinx

我们可以比较直观的感受到业务框架层中的异步代码同步化的直观、简单、以及没有回调。 而在基础设施层中,当我们需要使用协程,在使用suspend写入协程体,利用createCoroutine进行创建协程时,是比较复杂的,并且在构建Continuation内部类的过程中,我们发现还是会涉及到回调的使用。而在这之中,我们看到Continuation保存挂起点,而在Continuation中,实际上是CoroutineContext 协程上下文保存挂起点信息 我们可以进入Continuation的实现代码

@SinceKotlin("1.3")
public interface Continuation<in T> {
    /**
     * The context of the coroutine that corresponds to this continuation.
     */
    public val context: CoroutineContext
    /**
     * Resumes the execution of the corresponding coroutine passing a successful or failed [result] as the
     * return value of the last suspension point.
     */
    public fun resumeWith(result: Result<T>)
}

我们可以看到这是一个接口,而这之中,只有以一个CoroutineContext实例变量。因此、由协程上下文保存挂起点信息

协程调度器

而所有的协程都需要在协程调度器上才可以实现 在kotlin中,主要应用到了这几种协程调度器 调度器种类

  • Dispatchers.Main
  • Android上的主线程 用来处理UI交互和一些轻量级任务
  • 调用suspend函数
  • 调用UI函数
  • 更新LiveData
  • Dispatchers.Default
  • 非主线程 专为CPU密集型任务进行了优化
  • 数组排序
  • JSON数据解析
  • 处理差异判断
  • Dispatchers.IO
  • 非主线程 专为磁盘和系统IO进行了优化
  • 数据库
  • 网络
  • 文件读写

协程 任务泄漏

在我们的实现过程中,会出现以下场景 打开活动 to 开启协程进行网络请求 to 回退到上一活动 这个时候活动如果已经销毁,但是网络请求还在 会造成任务泄漏

这个时候,协程任务会丢失,导致无法追踪,会导致内存 CPU 磁盘等资源浪费 甚至发送 一个无用的网络请求 这种情况称之为任务泄漏

为了能够避免 任务泄漏 kotlin引入结构化并发机制

任务泄漏解决 结构化并发

结构化并发可以实现以下功能

  • 取消任务
  • 当某项任务不再需要时取消
  • 追踪任务
  • 当任务正在执行时 对任务进行追踪
  • 发出错误信号
  • 当协程失败时 发出错误信号表明有错误发生

定义协程必须指定CoroutineScope

它会跟踪所有协程 ,同样可以取消由它启动的所有协程

指定CoroutineScope便可以进行结构并发

  • GlobalScope 生命周期是process级别 即使Activity 或 Fragment已经被销毁 协程仍然在执行
  • MainScope 在 Activity中使用 可以在onDestory中取消协程
  • viewModelScope 只能在ViewModel中使用,绑定ViewModel的生命周期
  • lifecycleScope 只能在Activity、Fragment中使用 会绑定Activity和Fragment的生命周期

实战中如何验证结构化并发可以解决任务泄漏。

我们可以通过捕获异常来观察协程是否被取消 因为协程取消会抛出异常

Android小白,请多多指教。

内容如有错误,希望得到指点

目录
相关文章
|
6月前
|
Java 数据库 Android开发
【专栏】Kotlin在Android开发中的多线程优化,包括线程池、协程的使用,任务分解、避免阻塞操作以及资源管理
【4月更文挑战第27天】本文探讨了Kotlin在Android开发中的多线程优化,包括线程池、协程的使用,任务分解、避免阻塞操作以及资源管理。通过案例分析展示了网络请求、图像处理和数据库操作的优化实践。同时,文章指出并发编程的挑战,如性能评估、调试及兼容性问题,并强调了多线程优化对提升应用性能的重要性。开发者应持续学习和探索新的优化策略,以适应移动应用市场的竞争需求。
162 5
|
6月前
|
传感器 Android开发 开发者
构建高效Android应用:Kotlin的协程与Flow
【4月更文挑战第26天】随着移动应用开发的不断进步,开发者寻求更简洁高效的编码方式以应对复杂多变的业务需求。在众多技术方案中,Kotlin语言凭借其简洁性和强大的功能库逐渐成为Android开发的主流选择。特别是Kotlin的协程和Flow这两个特性,它们为处理异步任务和数据流提供了强大而灵活的工具。本文将深入探讨如何通过Kotlin协程和Flow来优化Android应用性能,实现更加流畅的用户体验,并展示在实际开发中的应用实例。
|
6月前
|
安全 Android开发 开发者
构建高效Android应用:Kotlin与协程的完美结合
【2月更文挑战第30天】在移动开发领域,性能优化和流畅的用户体验是关键。本文深入探讨了如何通过结合Kotlin语言和协程技术来提升Android应用的性能和响应能力。我们将分析Kotlin的优势,介绍协程的基本概念,并通过实际案例展示如何在应用中实现协程以简化异步编程,从而提供更加高效的解决方案。
|
2月前
|
安全 Java Android开发
Kotlin入门实用开发技巧与注意事项
本文源自公众号“AntDream”。Kotlin是由JetBrains开发的现代编程语言,自2017年成为Android官方开发语言后迅速流行。本文作者分享了Kotlin的实用技巧,包括变量声明、空安全、扩展函数等,帮助初学者避免常见问题。
72 15
|
1月前
|
Java 网络架构 Kotlin
kotlin+springboot入门级别教程,教你如何用kotlin和springboot搭建http
本文是一个入门级教程,介绍了如何使用Kotlin和Spring Boot搭建HTTP服务,并强调了Kotlin的空安全性特性。
58 7
kotlin+springboot入门级别教程,教你如何用kotlin和springboot搭建http
|
24天前
|
存储 前端开发 测试技术
Android kotlin MVVM 架构简单示例入门
Android kotlin MVVM 架构简单示例入门
28 1
|
1月前
|
JSON 调度 数据库
Android面试之5个Kotlin深度面试题:协程、密封类和高阶函数
本文首发于公众号“AntDream”,欢迎微信搜索“AntDream”或扫描文章底部二维码关注,和我一起每天进步一点点。文章详细解析了Kotlin中的协程、扩展函数、高阶函数、密封类及`inline`和`reified`关键字在Android开发中的应用,帮助读者更好地理解和使用这些特性。
20 1
|
3月前
|
调度 开发者 UED
Kotlin 中的协程是什么?
【8月更文挑战第31天】
221 0
|
5月前
|
监控 程序员 调度
协程实现单线程并发(入门)
协程实现单线程并发(入门)
62 1
|
5月前
|
存储 Java 调度
Android面试题之Kotlin 协程的挂起、执行和恢复过程
了解Kotlin协程的挂起、执行和恢复机制。挂起时,状态和上下文(局部变量、调用栈、调度器等)被保存;挂起点通过`Continuation`对象处理,释放线程控制权。当恢复条件满足,调度器重新分配线程,调用`resumeWith`恢复执行。关注公众号“AntDream”获取更多并发知识。
127 2