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小白,请多多指教。

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

目录
相关文章
|
1月前
|
Java 数据库 Android开发
【专栏】Kotlin在Android开发中的多线程优化,包括线程池、协程的使用,任务分解、避免阻塞操作以及资源管理
【4月更文挑战第27天】本文探讨了Kotlin在Android开发中的多线程优化,包括线程池、协程的使用,任务分解、避免阻塞操作以及资源管理。通过案例分析展示了网络请求、图像处理和数据库操作的优化实践。同时,文章指出并发编程的挑战,如性能评估、调试及兼容性问题,并强调了多线程优化对提升应用性能的重要性。开发者应持续学习和探索新的优化策略,以适应移动应用市场的竞争需求。
|
1月前
|
传感器 Android开发 开发者
构建高效Android应用:Kotlin的协程与Flow
【4月更文挑战第26天】随着移动应用开发的不断进步,开发者寻求更简洁高效的编码方式以应对复杂多变的业务需求。在众多技术方案中,Kotlin语言凭借其简洁性和强大的功能库逐渐成为Android开发的主流选择。特别是Kotlin的协程和Flow这两个特性,它们为处理异步任务和数据流提供了强大而灵活的工具。本文将深入探讨如何通过Kotlin协程和Flow来优化Android应用性能,实现更加流畅的用户体验,并展示在实际开发中的应用实例。
|
1月前
|
移动开发 Java Android开发
构建高效Android应用:Kotlin与协程的完美融合
【2月更文挑战第25天】 在移动开发领域,性能优化和应用响应性的提升是永恒的追求。随着Android Jetpack组件库的不断丰富,Kotlin语言已经成为Android开发的首选。而Kotlin协程作为一种新的并发处理方案,它以轻量级线程的形式,为开发者提供了简洁高效的异步编程手段。本文将深入探讨Kotlin协程在Android应用中的实践运用,以及如何通过这种技术改善用户界面的流畅度和后台任务的处理能力,进而构建出更高效、更稳定的Android应用。
|
1月前
|
安全 Android开发 开发者
构建高效Android应用:Kotlin与协程的完美结合
【2月更文挑战第30天】在移动开发领域,性能优化和流畅的用户体验是关键。本文深入探讨了如何通过结合Kotlin语言和协程技术来提升Android应用的性能和响应能力。我们将分析Kotlin的优势,介绍协程的基本概念,并通过实际案例展示如何在应用中实现协程以简化异步编程,从而提供更加高效的解决方案。
|
7天前
|
监控 程序员 调度
协程实现单线程并发(入门)
协程实现单线程并发(入门)
12 1
|
13天前
|
存储 Java 调度
Android面试题之Kotlin 协程的挂起、执行和恢复过程
了解Kotlin协程的挂起、执行和恢复机制。挂起时,状态和上下文(局部变量、调用栈、调度器等)被保存;挂起点通过`Continuation`对象处理,释放线程控制权。当恢复条件满足,调度器重新分配线程,调用`resumeWith`恢复执行。关注公众号“AntDream”获取更多并发知识。
22 2
|
1月前
|
移动开发 Android开发 开发者
构建高效Android应用:Kotlin与协程的完美融合
【5月更文挑战第25天】 在移动开发的世界中,性能和响应性是衡量应用质量的关键指标。随着Kotlin的流行和协程的引入,Android开发者现在有了更强大的工具来提升应用的性能和用户体验。本文深入探讨了Kotlin语言如何与协程相结合,为Android应用开发带来异步处理能力的同时,保持代码的简洁性和可读性。我们将通过实际案例分析,展示如何在Android项目中实现协程,以及它们如何帮助开发者更有效地管理后台任务和用户界面的流畅交互。
|
1月前
|
移动开发 数据库 Android开发
构建高效Android应用:探究Kotlin的协程优势
【5月更文挑战第22天】随着移动开发技术的不断进步,Android平台的性能优化已经成为开发者关注的焦点。在众多提升应用性能的手段中,Kotlin语言提供的协程概念因其轻量级线程管理和异步编程能力而受到广泛关注。本文将深入探讨Kotlin协程在Android开发中的应用,以及它如何帮助开发者构建出更高效、响应更快的应用,同时保持代码的简洁性和可读性。
|
1月前
|
移动开发 监控 Android开发
构建高效安卓应用:Kotlin 协程的实践与优化
【5月更文挑战第16天】 在移动开发领域,性能优化一直是开发者们追求的重要目标。特别是对于安卓平台来说,由于设备多样性和系统资源的限制,如何提升应用的响应性和流畅度成为了一个关键议题。近年来,Kotlin 语言因其简洁、安全和高效的特点,在安卓开发中得到了广泛的应用。其中,Kotlin 协程作为一种轻量级的并发解决方案,为异步编程提供了强大支持,成为提升安卓应用性能的有效手段。本文将深入探讨 Kotlin 协程在安卓开发中的应用实践,以及通过合理设计和使用协程来优化应用性能的策略。
30 8
|
1月前
|
移动开发 Android开发 开发者
构建高效安卓应用:Kotlin 协程的实践指南
【5月更文挑战第18天】 随着移动开发技术的不断进步,安卓平台亟需一种高效的异步编程解决方案来应对日益复杂的应用需求。Kotlin 协程作为一种新兴的轻量级线程管理机制,以其简洁的语法和强大的功能,成为解决这一问题的关键。本文将深入探讨Kotlin协程在安卓开发中的实际应用,从基本概念到高级技巧,为开发者提供一份全面的实践指南,旨在帮助读者构建更加高效、稳定的安卓应用。