Android kotlin实战之协程suspend详解与使用

news2024/11/15 9:10:53

前言

        Kotlin 是一门仅在标准库中提供最基本底层 API 以便各种其他库能够利用协程的语言。与许多其他具有类似功能的语言不同,async 与 await 在 Kotlin 中并不是关键字,甚至都不是标准库的一部分。此外,Kotlin 的 挂起函数 概念为异步操作提供了比 future 与 promise 更安全、更不易出错的抽象。

        kotlinx.coroutines 是由 JetBrains 开发的功能丰富的协程库。它包含本指南中涵盖的很多启用高级协程的原语,包括 launch、 async 等等。

 如需了解其他kotlin用法,可查看如下:

Android Kotlin实战之高阶使用泛型扩展协程懒加载详解_蜗牛、Z的博客-CSDN博客

Android kotlin在实战过程问题总结与开发技巧详解_kotlin 同步锁_蜗牛、Z的博客-CSDN博客

Kotlin语法详解与实践教程,区分JAVA以及如何闭坑_kotlin mapof可以嵌套 to_蜗牛、Z的博客-CSDN博客

什么是协程suspend

        suspend的字面含义是暂停、挂起的意思。在kotlin中,代码执行到 suspend 函数的时候会『挂起』,并且这个『挂起』是非阻塞式的,它不会阻塞你当前的线程,挂起的定位: 暂时切走,稍后再切回来,和java的Thread.sleep()不一样,一个是阻塞,一个是等待,类似wait。

阻塞的基本原理一直占用CUP,不让出如果sleep,挂起的是将自己的cup时间让出去,等待重新分配。这样就很好理解了。用最直观的理解是一种异步操作

协程方法的调用

如果当前方法被suspend修饰了,如果不是通过runblock或者coroutines 库调用,那么调用链上的所有函数都要背suspend修饰

1.修饰方法

suspend fun main() {
    delay(1000)
    println("hello")
}

又因为我们在业务中,很难会获取到main方法,所以无法通过main方法在源头解决协程的调用

2.借助工具类

2.1同步方法runBlocking

runBlocking是阻塞调用,如果通过它来处理协程,那么只有当runBlocking的方法体中执行完,才会往下执行,会一直霸占cpu,直到运行完才会释放。

 fun main() {
    runBlocking {
        delay(1000)
        println("hello---block")
    }
    println("hello")
}

 2.2异步库kotlinx.coroutines 

异步库的调用是先挂起线程代码,继续执行,等下面执行完才会又回到协程函数执行

fun main() {
    GlobalScope.launch { // 在后台启动一个新的协程并继续
        println("suspend start!") // 在延迟后打印输出
        delay(1000L) // 非阻塞的等待 1 秒钟(默认时间单位是毫秒)
        println("suspend end!") // 在延迟后打印输出
    }

    println("sleep start,") // 协程已在等待时主线程还在继续
    Thread.sleep(2000L) // 阻塞主线程 2 秒钟来保证 JVM 存活
    println("sleep end,") // 协程已在等待时主线程还在继续


}

 

通过以上日志分析,协程提都不会被执行,被挂起了,先执行了下面的代码,遇到sleep函数,形成阻塞,协程又获取到cpu的时间,开始执行协程代码快,执行完,sleep阻塞完成又继续执行。

coroutines 库的使用

kotlinx.coroutines是填补了kotlin在Java中的缺失,也提供了异步处理协程的问题。

依赖库:

   implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.1"

使用

1、异步挂起协程

    GlobalScope.launch { // 在后台启动一个新的协程并继续
       
        //协程代码调用处
       
    }

2、延迟

    GlobalScope.launch { // 在后台启动一个新的协程并继续

        delay(1000L) // 非阻塞的等待 1 秒钟(默认时间单位是毫秒)

    }

直接调用delay,设置需要阻塞延迟的时间

3、工作对象Job

launch会返回一个对象Job,job就是当前的任务,如果你接受了这个Job,那么代码块将不会执行,需要你手动调用

fun main() {
    val job = GlobalScope.launch { // 启动一个新协程并保持对这个作业的引用
        delay(1000L)
        println("World!")
    }

    println("Hello,")
    runBlocking {
        job.join() // 等待直到子协程执行结束
    }

    
}

job的join只能在阻塞方法中调用,或者suspend方法体,不能在

GlobalScope.launch {
    job.join()
}

调用,否则不会执行当前代码块。因为launch已返回了一个job,当前job没有执行,所以,改job一直处于一个未执行状态。

也可以取消:job.cancel()

 协程作用域:launch{}

GlobalScope与CoroutineScope对比

八股文显示,前者比后者快,通过源码分析,GlobalScope是继承了CoroutineScope,是对CoroutineScope的封装,提供一个静态函数。

结构化的并发

        协程的实际使用还有一些需要改进的地方。 当我们使用 GlobalScope.launch 时,我们会创建一个顶层协程。虽然它很轻量,但它运行时仍会消耗一些内存资源。有一个更好的解决办法。我们可以在代码中使用结构化并发。 我们可以在执行操作所在的指定作用域内启动协程, 而不是像通常使用线程(线程总是全局的)那样在 GlobalScope 中启动.

fun main() = runBlocking { // this: CoroutineScope
    launch { // 在 runBlocking 作用域中启动一个新协程
        delay(1000L)
        println("World!")
    }
    println("Hello,")

 
}

作用域构建器

        除了由不同的构建器提供协程作用域之外,还可以使用 coroutineScope 构建器声明自己的作用域。它会创建一个协程作用域并且在所有已启动子协程执行完毕之前不会结束.

        runBlocking 与 coroutineScope 可能看起来很类似,因为它们都会等待其协程体以及所有子协程结束。 主要区别在于,runBlocking 方法会阻塞当前线程来等待, 而 coroutineScope 只是挂起,会释放底层线程用于其他用途。 由于存在这点差异,runBlocking 是常规函数,而 coroutineScope 是挂起函数

fun main() = runBlocking { // this: CoroutineScope
    launch {
        delay(200L)
        println("Task from runBlocking 1")
    }

    coroutineScope { // 创建一个协程作用域
        launch {
            delay(500L)
            println("Task from nested launch 2")
        }

        delay(100L)
        println("Task from coroutine scope 3") // 这一行会在内嵌 launch 之前输出
    }

    println("Coroutine scope is over 4") // 这一行在内嵌 launch 执行完毕后才输出
}

 挂起 :GlobalScope.async

GlobalScope的async异步挂起,支持返回值,且不能单独直接使用,需在runBlocking体内执行。

fun main()= runBlocking {
    println("current thread 1")
    //launch启动后台调度线程,并且不堵塞当前线程
    val deferred = GlobalScope.async {
        println("async thread 2=")
        delay(1000)
        println("async end 3")
        //需要通过标签的方式返回
        return@async "123"
    }
    println("current thread end 4")
    val result = deferred.await()
    println("result 5= $result")
    //当前线程休眠以便调度线程有机会执行
    Thread.sleep(3000)
}

协程上下文与调度器

        协程上下文包含一个 协程调度器 (参见 CoroutineDispatcher)它确定了相关的协程在哪个线程或哪些线程上执行。协程调度器可以将协程限制在一个特定的线程执行,或将它分派到一个线程池,亦或是让它不受限地运行。

launch { // 运行在父协程的上下文中,即 runBlocking 主协程
    println("main runBlocking      : I'm working in thread ${Thread.currentThread().name}")
}
launch(Dispatchers.Unconfined) { // 不受限的——将工作在主线程中
    println("Unconfined            : I'm working in thread ${Thread.currentThread().name}")
}
launch(Dispatchers.Default) { // 将会获取默认调度器
    println("Default               : I'm working in thread ${Thread.currentThread().name}")
}
launch(newSingleThreadContext("MyOwnThread")) { // 将使它获得一个新的线程
    println("newSingleThreadContext: I'm working in thread ${Thread.currentThread().name}")
}

调度器类型如下:

public actual object Dispatchers {

    @JvmStatic
    public actual val Default: CoroutineDispatcher = DefaultScheduler


    @JvmStatic
    public actual val Main: MainCoroutineDispatcher get() = MainDispatcherLoader.dispatcher


    @JvmStatic
    public actual val Unconfined: CoroutineDispatcher = kotlinx.coroutines.Unconfined

    @JvmStatic
    public val IO: CoroutineDispatcher = DefaultIoScheduler

    @DelicateCoroutinesApi
    public fun shutdown() {
        DefaultExecutor.shutdown()
        // Also shuts down Dispatchers.IO
        DefaultScheduler.shutdown()
    }
}

调试协程与线程

        协程可以在一个线程上挂起并在其它线程上恢复。 如果没有特殊工具,甚至对于一个单线程的调度器也是难以弄清楚协程在何时何地正在做什么事情。

启动方式   

runBlocking创建新的协程,运行在当前线程上,所以会堵塞当前线程,直到协程体结束
GlobalScope.launch启动一个新的线程,在新线程上创建运行协程,不堵塞当前线程
GlobalScope.asyn启动一个新的线程,在新线程上创建运行协程,并且不堵塞当前线程,支持 通过await获取返回值

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/382503.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

π型滤波器 计算_π型滤波电路

滤波器在功率和音频电子中常用于滤除不必要的频率。而电路设计中,基于不同应用有着许多不同种类的滤波器,但它们的基本理念都是一致的,那就是移除不必要的信号。所有滤波器都可以被分为两类,有源滤波器和无源滤波器。有源滤波器用…

重新认识 Java 中的内存映射(mmap)

mmap 基础概念 mmap 是一种内存映射文件的方法,即将一个文件映射到进程的地址空间,实现文件磁盘地址和一段进程虚拟地址的映射。实现这样的映射关系后,进程就可以采用指针的方式读写操作这一段内存,而系统会自动回写脏页到对应的文…

电源程控软件下载安装教程

软件:电源程控软件NS-PowerSupply 语言:简体中文 环境:NI-VISA 安装环境:Win10以上版本(特殊需求请后台私信联系客服) 硬件要求:CPU2GHz 内存4G(或更高)硬盘500G(或更高&#xf…

2023年2月安全事件盘点

一、基本信息 2023年2月安全事件共造成约3796万美元损失,相较于上个月,安全事件数量与损失金额都有显著上升,其中Platypus Finance闪电贷攻击为单次利用损失之最高达850万美元。本月RugPull数量基本与上月持平,损失金额占比显著降…

网站打不开数据库错误等常见问题解决方法

1、“主机开设成功!”上传数据后显示此内容,是因为西部数码默认放置的index.htm内容,需要核实wwwroot目录里面是否有自己的程序文件,可以删除index.htm。 2、恭喜,lanmp安装成功!这个页面是wdcp的默认页面&…

用 Real-ESRGAN 拯救座机画质,自制高清版动漫资源

内容一览:Real-ESRGAN 是 ESRGAN 升级之作,主要有三点创新:提出高阶退化过程模拟实际图像退化,使用光谱归一化 U-Net 鉴别器增加鉴别器的能力,以及使用纯合成数据进行训练。 关键词:Real-ESRGAN 超分辨率 视…

一文彻底搞懂cookie、session、token、jwt!

前言 随着Web应用程序的出现,直接在客户端上存储用户信息的需求也随之出现。者背后的想象时合法的:与特定用户相关的信息都应该保存在用户的机器上。无论是登录信息、个人偏好、还是其他数据,Web应用程序提供者都需要有办法 将他们保存在客户…

电子技术——CMOS 逻辑门电路

电子技术——CMOS 逻辑门电路 在本节我们介绍如何使用CMOS电路实现组合逻辑函数。在组合电路中,电路是瞬时发生的,也就是电路的输出之和当前的输入有关,并且电路是无记忆的也没有反馈。组合电路被大量的使用在当今的数字逻辑系统中。 晶体管…

Educational Codeforces Round 144 (Rated for Div. 2)(A~C)

A. Typical Interview Problem从1开始,遇到3的倍数就在字符串后面加F,遇到5的倍数就在字符串后面加B,若遇到3和5的倍数,就加入FB,这样可以写一个无限长的字符串,给出一个长度最多为10的字符串,判…

CLion+Opencv+QT开发相关

一、QT安装和配置其实我并没有直接在Qt上开发,下载Qt而是因为:CLion可以通过Qt的MinGW作为Toolset,并且可以将Qt creator作为external tool;在进行Opencv的编译安装中可以用Qt自带的MinGW进行编译和安装,不用另外下载M…

C++类和对象:初始化列表、static成员和友元

目录 一. 初始化列表 1.1 对象实例化时成员变量的创建及初始化 1.2 初始化列表 1.3 使用初始化列表和在函数体内初始化成员变量的效率比较 1.4 成员变量的初始化顺序 1.5 explicit关键字 二. static成员 2.1 static属性的成员变量 2.2 static属性的成员函数 三. 友元 …

废气处理设备远程监控

当今工业迅速的发展,工业带给人们的经济效益显著,而同时污染问题也备受关注。国家环保标准对排放至大气的废气指标提出了更高的要求。面临着环保压力,企业为走可持续发展之路,为维护员工利益、改善工作环境及周边环境不受影响&…

一、Sping框架引入

OCP开闭原则 什么是OCP? OCP是软件七大开发原则当中最基本的一个原则:开闭原则 对什么开?对扩展开放。 对什么闭?对修改关闭。OCP原则是最核心的,最基本的,其他的六个原则都是为这个原则服务的。OCP开闭原则…

计算机行业回暖?看网友怎么说?

就业寒潮之下,去年的应届生们可谓哀嚎一片,不少人晒出自己的0offer秋招战绩。 就连过去无往不利的计算机行业,亦不例外。但今年开始,计算机行业逐渐有了回暖的迹象和讨论。 陆续有不少之前哭诉收获惨淡的计算机专业同学&#x…

防静电和浪涌TVS layout设计要点

电子产品精密化刚看过了CES2023,雷卯的外贸伙伴们看了最新的AR,VR,5G产品,新的电子产品更智能、更复杂,嵌入了脆弱和敏感的集成电路。这些设备的环境往往很恶劣,产生高水平静电和快速瞬态浪涌。这些ESD事件可能会干扰设备&#xf…

IIS之web服务器的安装、部署以及使用教程(图文详细版)

WEB服务器的部署 打开虚拟机后查看已经开放的端口,可以看到没有TCP 80、TCP 443,说明HTTP服务端口没有打开 打开我的电脑—双击CD驱动器 选择安装可选的Windows组件 选择应用程序服务器—打开Internet信息服务—选择万维网服务和FTP服务 一路确…

uniapp-首页配置

为了获取到后台服务器发来的数据,需要配置相应的网络地址。位置在main.js入口文件中。 import { $http } from escook/request-miniprogramuni.$http $http // 配置请求根路径 $http.baseUrl https://api-hmugo-web.itheima.net// 请求开始之前做一些事情 $http.…

Spring-Xml配置

一、Spring 简介 1.简介 文档下载地址:Index of /spring-framework/docs 1.简介 Spring framework 是 Spring 基础框架 学习Spring 家族产品 Spring framework SpringBoot SpringCloud Spring 能用来做什么 开发 WEB 项目 微服务 分布式系统 Spring framew…

云服务HCIE变题当天一把过!分享下学习备考和考试经验

大家好,我是誉天云服务学员刘同学。感谢在誉天的学习,让我在临考变题的情况下通过了云服务HCIE考试;也感谢誉天给我这次机会分享出学习备考和考试的经验。 算起来,我和誉天也是老朋友了:一开始是跟着邹老师学习云计算、…

【JAVA】线程和进程

🏆今日学习目标:线程和进程 😃创作者:颜颜yan_ ✨个人主页:颜颜yan_的个人主页 ⏰本期期数:第三期 🎉专栏系列:JAVA 线程和进程前言一、进程与线程1.进程2.线程二、线程的创建2.1 继…