Kotlin协程概览

2023-11-15

  协程( Coroutines)并不是 Kotlin 提出来的新概念,很多的编程语言都有实现,如:Go、Python 等。。本文所讲,专指kotlin的协程。

  在Android 11中,Asynctask已经被废弃了,因为协程可以更简单,直观的实现异步任务。而且协程是谷歌推荐的异步处理机制,那么什么是协程呢?其实很简单,就是kotlint封装的一套线程api(线程框架),类似于 Java 中的 Executor 和 Android &Java中的 AsyncTask、Handler。
  协程的创建方式有三种,async{},launch{}和runBlocking{}。如下:

launch{ getName() }

var name = async{ getName() } 
Log.i(TAG, "name1 is " + name.await())

runBlocking{ getName() }

  runBlocking的方式,在开发中基本不会用到,因为它是线程阻塞的。launch是最常用的,async有时也会用到。用async创建,会有一个 Deferred 类型的返回值,需要使用 返回值.await()来获取返回值,如果不用,不等async返回,就执行下面的打印语句了。
  但是launch和async不可以直接使用,需要在协程作用域里面才能用,有下面三种方式创建协程作用域。

runBlocking {
    getName()
}
//通过使用 GlobalScope 单例的方式
GlobalScope.launch {
    getName()
}

// 参数类型为 CoroutineContext ,Dispatchers.Main 和 Job 都是CoroutineContext 类型
val scope = CoroutineScope(Dispatchers.Main + Job())
//取消协程:job.cancle() 判断状态:job.isActive job.isCancelled isCompleted
val job = scope.launch {
    getName()
}

  runBlocking的方式上面说过了,方法二不会阻塞线程,但是也不推荐这种用法,因为它的生命周期和 app 一样,且不能取消,推荐使用第三种方式。来看下CoroutineScope的参数。
  Dispatcher 用于告知协程,应该在哪个线程中运行。
Dispatchers.Main 就是在 Android 的主线程中运行,除了 Main 之外,我们还可以指定:
Dispatchers.IO:对磁盘和网络 IO 进行了优化,适合 IO 密集型的任务,如:读写文件,操作数据库和网络请求
Dispatchers.Default:适合 CPU 密集型的任务,比如运算
  Job是协程的唯一身份标识,可以用来控制和判断协程的状态。
  下面来具体看看协程到底有多简洁,方便。(注:launch{}代码块就是协程)

  val scope = CoroutineScope(Job()+Dispatchers.Main)
  val job1 = scope.launch {
  	// xxxxxxxxx location 1 运行在主线程
  	var name =  getName() // location 2 运行在IO线程
  	var pwd = getPwd() // location 3 运行在IO线程
  	textView1 = name //location 4 运行在主线程
  	textView2 = pwd
  }
  private suspend fun getName() = withContext(Dispatchers.IO) {
	//        delay(4000)
    val name: String = getNameFromNetwork();//耗时操作
    name
  }
  private suspend fun getPwd() = withContext(Dispatchers.IO) {
	//        delay(4000)
    val pwd: String = getPwdFromNetwork();//耗时操作
    pwd
  }

  以前进行网络操作时,基本都是用的回调,有了协程,我们就可以直接获取,然后直接使用。而且不同的线程可以写在一处,这就是神奇的协程!
  有几处需要说明一下:

withContext()是kotlin内置的suspend(挂起函数),可以指定它所包含的代码块运行在哪个线程,很常用的,代码块的最后一行就是返回值。
getName()是自定义的挂起函数,需要在挂起函数或者协程中调用。

  说下这段代码的执行流程:

当执行到location 1时,运行在主线程
当执行到location 2时,就兵分两路了,一路是主线程,一路是协程(此处是IO线程)
主线程跳出协程,也就是跳出launch{}代码块,执行后续代码
协程(IO线程)就可以执行它的耗时的动作了
IO线程执完毕时,切回到主线程,执行location 4

  所以协程的本质就是启动一个新线程,然后执行新的线程,也就是suspend函数(不影响主线程,主线程该干嘛干嘛),等新的线程执行完毕,再切回(合并)到主线程,也就是resume。
  为什么suspend函数直接或者间接的被协程调用呢?这是因为切换回主线程的动作只有协程才能做。这么看来,suspend函数的作用只是切换到IO线程咯,没那么简单!在getName()函数中也可以写withContext(Dispatchers.Main),那就不用切线程了,那么suspend函数到底有什么用呢?其实就是一个提醒,suspend函数的创建者提醒调用者–本函数可能会有耗时操作,需要在协程里面调用。
  为什么都说suspend函数是非阻塞式挂起呢?道理很简单,因为挂起不影响主线程啊,只不过是开启了一个新的线程做耗时操作而已。需要说明的是,自定义 的suspend函数,需要调用系统自带的挂起函数,如果不调用,那么它就没有存在的意义了。
  其实上面的代码还有改进的空间,getPwd()是要等getName()执行完毕才会接着执行,因为它俩运行在同一个线程,这样显然是有些浪费时间的,所以可以进行如下改造:

  var name =  async{ getName() } // location 2 运行在IO线程
  var pwd = async{ getPwd() } // location 3 运行在IO线程
  textView1 = name.await() //location 4 运行在主线程
  textView2 = pwd.await()

  这样在协程内部又创建了两个子协程,如此一来,getPwd()和getName()就运行在两个不同的线程,而且使用了await()就是等asuync{}返回了再去赋值。
  协程内部再创建协程,那么新创建的协程就是原来协程的子协程,举例说明:

  scope = CoroutineScope(Job()+Dispatchers.Main)
  val job1 = scope.launch { // No.1
  	// xxxxxxx location 1
  	var name =  getName() 
  	// xxxxxxx location 2
  	var job2 = launch{ // No.2
		var jo4 = launch{ // No.4
		}
	}
	var job3 = launch{ // No.3
	}
  }
  private suspend fun getName() = withContext(Dispatchers.IO) {
  	if(!scope.isCancelled) { // ensureActive()
		//        delay(4000)
	    val name: String = getNameFromNetwork();//耗时操作
	    name
    } else {
    	""
    }
  }

  上面的代码中一个创建了4个协程,No.2 No.3的父协程是No.1,它俩是兄弟协程,No.4的父协程是No.2。协程的这种特性叫做结构化并发,这个特性,使得管理起来很是方便,比如,取消所有协程,只需要调用:scope.cancel()就可以了。要取消No.2 No.4,只需要调用 job2.cancel()。取消操作是取消自己和它的子协程。
  如果协程正在执行时被取消了,拿上面的例子来说,如果执行到location 1,协程被取消,那么后面的都不执行了,如果正在执行getName()那么会执行完该函数,后面的不再执行,等getName()执行完毕,系统还会再自动检测协程是否完成(withContext的功能),如果完成了,后面的也不执行了。
  kotlin官方有这么一个对比:

repeat(100000) {
  launch {
    delay(1000)
    println(".")
  }
}
repeat(100000){
  Thread{
    Thread.sleep(1000)
      println(".")
  }
}

  开启十万个协程与开启十万个线程对比,协程可以正常执行,而线程却出现了内存溢出,官方以此来说明协程比线程更加轻量级,其实这是不对的,因为协程是开启了一个线程池来运行线程的,所以正确对比应该是下面这样:

val executors = Executors.newSingleThreadScheduledExecutor()
var task = Runnable{
  println(".")
}
repeat(100000) {
  executors.schedule(task, 1, TimeUnit.SECONDS)
}

协程 VS 线程池,性能不相上下。
  最后再来说下使用协程所需要的依赖吧!
根目录下的 build.gradle :

buildscript {
  ...
  ext.kotlin_coroutines = '1.3.1'
  ...
}

Module 下的 build.gradle :

dependencies {
  ...
  // 依赖协程核心库
  implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlin_coroutines"
  // 依赖当前平台所对应的平台库
  implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlin_coroutines"
  ...
}

  好了,到这里,kotlin的协程就讲的差不多了,来总结一下,kotlin协程就是一个线程框架,它可以启动并且切换线程,而且在线程执行完毕后,还可以再切回来,代码看起来很简洁,用阻塞式的代码实现了非阻塞的效果。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Kotlin协程概览 的相关文章

  • GCM 向主题发送消息:TOO_MANY_TOPICS 错误

    以前 GCM 每个应用程序有 100 万个主题订阅的限制 我发现他们现在已经取消了这一限制 基于发布 订阅模型 主题消息支持 每个应用程序无限订阅 https developers google com cloud messaging to
  • Android - 如何一次只允许播放一个 MediaPlayer 实例?

    我正在尝试创建一个简单的 Sound board Android 应用程序 使用 ListView 项目作为按钮 顺便说一句 我是一个新手程序员 我的想法是 我按下一个按钮 就会播放一个特定的声音文件 如果我在播放声音时按下任何按钮 它应该
  • Firebase Analytics 禁用受众国家/地区跟踪

    我正在开发一个严格不允许位置跟踪的应用程序 我想使用 Firebase Analytic 的其他功能 例如 PageTransitions 和 Crashalitics 但如果我无法禁用受众位置跟踪 我就无法使用其中任何功能 这是我在 An
  • 为什么将函数参数声明为最终的?

    我目前正在阅读 Sams 出版的 24 小时自学 Android 应用程序开发 一书 我对 Java Android 或其他方面还比较陌生 我对 ActionScript 3 有非常扎实的背景 它与 Java 有足够的相似之处 因此该语言本
  • Phonegap - 如何将.txt文件保存在Android手机的根目录中

    我正在尝试使用phonegap 将 txt 文件保存在Android 手机的根目录中 我已经安装了这些插件 cordova plugin file 和 cordova plugin file transfer 在 config xml 文件
  • 如何将 Java 赋值表达式转换为 Kotlin

    java中的一些东西就像 int a 1 b 2 c 1 if a b c System out print true 现在它应该转换为 kotlin 就像 var a Int 1 var b Int 2 var c Int 1 if a
  • KitKat(及更低版本)设备上的 Android Material Design

    我将在我们学校开发一个 Android 应用程序作为一个项目 我想使用 Google 的新 Material Design 但我知道它仅适用于 Android L 设备 Jack Underwood 最近发布了名为 Today Calend
  • 接近语法错误(代码1)插入Android SQLite

    我正在创建一个通讯录应用程序 用户可以在其中输入姓名 电子邮件地址和号码 我希望将此数据保存在数据库中 但我似乎无法使插入方法起作用 我收到的错误是 android database sqlite SQLiteException near
  • Android Studio 在编译时未检测到支持库

    由于 Android Studio 将成为 Android 开发的默认 IDE 因此我决定将现有项目迁移到 Android studio 中 项目结构似乎不同 我的项目中的文件夹层次结构如下 Complete Project gt idea
  • 在意图过滤器中使用多个操作时的默认值

    尝试理解 Android 中的意图和操作并查看文档 http developer android com guide topics intents intents filters html 但我一直看到的一件事是定义了多个操作的意图过滤器
  • 在我的Android中,当其他应用程序想要录制音频时如何停止录音?

    在我的应用程序中 服务通过 AudioRecord 持续录制音频 当我的应用程序运行时 其他与音频记录相关的应用程序 例如 Google 搜索 无法工作 如何知道何时有其他应用想要录制音频 以便我可以停止录制以释放资源 答案是MediaRe
  • MediaCodec 创建输入表面

    我想使用 MediaCodec 将 Surface 编码为 H 264 使用 API 18 有一种方法可以通过调用 createInputSurface 然后在该表面上绘图来对表面中的内容进行编码 我在 createInputSurface
  • Android构建apk:控制MANIFEST.MF

    Android 构建 APK 假设一个 apk 包含一个库 jar 例如 foo jar 该库具有 META INF MANIFEST MF 这对于它的运行很重要 但在APK中有一个包含签名数据的MANIFEST MF 并且lib jar
  • 材质设计图标颜色

    应该是哪种颜色 暗 材质图标 在官方文档上 https www google com design spec style icons html icons system icons https www google com design s
  • 下载后从谷歌照片库检索图像

    我正在发起从图库中获取照片的意图 当我在图库中使用 Nexus 谷歌照片应用程序时 一切正常 但如果图像不在手机上 在 Google Photos 在线服务上 它会为我下载 选择图像后 我将图像发送到另一个活动进行裁剪 但在下载的情况下 发
  • Android中webview的截图方法

    我在 webview 中的 html5 canvas 上画了一些线 并尝试使用下面的代码截取 webview 的屏幕截图 WebView webView WebView findViewById R id webview webView s
  • 保护 APK 中的字符串

    我正在使用 Xamarin 的 Mono for Android 开发一个 Android 应用程序 我目前正在努力使用 Google Play API 添加应用内购买功能 为此 我需要从我的应用程序内向 Google 发送公共许可证密钥
  • Android:有没有办法以毫安为单位获取设备的电池容量?

    我想获取设备的电池容量来进行一些电池消耗计算 是否可以以某种方式获取它 例如 三星 Galaxy Note 2 的电池容量为 3100mAh 谢谢你的帮助 知道了 在 SDK 中无法直接找到任何内容 但可以使用反射来完成 这是工作代码 pu
  • Android 如何聚焦当前位置

    您好 我有一个 Android 应用程序 可以在谷歌地图上找到您的位置 但是当我启动该应用程序时 它从非洲开始 而不是在我当前的城市 国家 位置等 我已经在developer android com上检查了信息与位置问题有关 但问题仍然存在
  • 使用 Espresso 检查 EditText 的字体大小、高度和宽度

    如何使用 Espresso 检查 EditText 的字体大小 高度和宽度 目前要分割我使用的文本 onView withId R id editText1 perform clearText typeText Amr 并阅读文本 onVi

随机推荐

  • python 爬虫 requests模块 中的Cookies 验证 通过验证cookies模拟登陆豆瓣登陆

    在爬取某些数据时 需要进行网页的登陆 才可以进行数据的抓取工作 Cookies登陆就像很多网页中的自动登陆功能一样 可以让用户第二次登陆时不在需要验证账号和密码的情况下进行登陆 在requests模块中实现Cookies登陆时 首先需要在浏
  • 华为OD机试真题 Java 实现【异常的打卡记录】【2023Q1 100分】

    一 题目描述 考勤记录是分析和考核职工工作时间利用情况的原始依据 也是计算职工工资的原始依据 为了正确地计算职工工资和监督工资基金使用情况 公司决定对员工的收集打卡记录进行异常排查 如果出现以下两种情况 则认为打卡异常 实际设备号与注册设备
  • selenium+pytest——失败用例重试

    selenium pytest 失败用例重试 一 目的 在我们使用selenium pytest做UI自动化的时候偶尔会遇到因为特殊情况 比如浏览器加载失败 网络波动等等导致用例运行失败 可能单独运行没 问题 对于这些场景产生的用例结果不是
  • UE4 蓝图通信:接口调用

    UE4学习心得 蓝图间信息通信的几种方法 UE4的接口调用技术有点简单粗暴 而且主要体现在主蓝图对子蓝图的信息通信 在内容浏览器中添加一个蓝图接口 命名为TestInterface 双击打开接口 直接使用其创建时自带的一个接口函数 将其重命
  • 物理机安装centos7(u盘安装)——详细版

    我用的是华为的物理机 其它物理机操作几乎相同 可能不同的设置调试方法不同 如果是虚拟机安装 直接跳到centos7设置即可 物理机U盘启动 安装centos8方法相同 可能有些需要硬件配置相关 相关问题看具体报错方式 UltraISO下载地
  • 在C语言中 ¬∧∨这些符号什么意思

    b b b a a a b a a 或运算是 a b a b b b a a a 这三个都是位运算 是取非运算 交你个小窍门 没啥子好多的了 好好看看 里面有详细的解释 这就是在逻辑运算中常用到的短路判断 ls的已经说的很清楚了 b a b
  • 微信小程序之首页搭建

    小程序开发与实战 学习视频 https www bilibili com video BV1Gv411g7j6 p 9 spm id from pageDriver 实现导航栏和tabBar 实现导航栏和tabBar tabBar看下图 参
  • 电荷泵

    电荷泵 又称为电容式的开关稳压器 或开关电容DC DC变换器 无感式DC DC变换器 电荷泵采用电容作为开关和储能的元件 如图所示 S1与S3闭合 S2与S4断开 则Vin给电容充电 而后S1与S3断开 S2与S4闭合 则电容放电 此时Vo
  • Virtual Judge-4099:队列和栈

    Virtual Judge 4099 队列和栈 题目描述 队列和栈是两种重要的数据结构 它们具有push k和pop操作 push k是将数字k加入到队列或栈中 pop则是从队列和栈取一个数出来 队列和栈的区别在于取数的位置是不同的 队列是
  • PyTorch入门(六)使用Transformer模型进行中文文本分类

    在文章PyTorch入门 五 使用CNN模型进行中文文本分类中 笔者介绍了如何在PyTorch中使用CNN模型进行中文文本分类 本文将会使用Transformer模型实现中文文本分类 本文将会使用相同的数据集 文本预处理已经在文章PyTor
  • C语言程序——用星号打印图案

    文章目录 前言 一 用星号打印图案 二 程序实例 1 程序代码 2 运行结果 3 结果分析 三 拓展应用 总结 前言 用打印字符来输出星号组成的HELLO 一 用星号打印图案 用星号打印图案 一般利用星号画出具体的模拟输出形式 然后在输出时
  • 【Android】常用对话框大全(一)Android Dialog

    Android的对话框有多少种 Android好看的对话框有很多 如Android material qmui xui kongzue等系列对话框 但博主只打算讲解Android material系列对话框 讲太多没必要 实在想要做成人家那
  • 千万级数据清洗ETL设计方案

    千万级数据清洗项目分析总结 项目简介 一 需求分析 1 前期需求 2 中期需求 3 后期需求 二 技术支持 1 MySQL 2 Redis 三 框架设计 1 流线型代码 2 工厂模式 四 调式工作 1 线上测试 五 问题回顾 1 Mysql
  • scratch python的区别ev3_机器人编程和少儿编程,傻傻分不清—乐高EV3入门感想

    机器人编程和少儿编程的区别 机器人编程和少儿编程不是一个概念 机器人编程是少儿编程的重要组成部分 少儿学习编程大体上是两种方式 1 纯软件 最具代表性的是scratch 是麻省理工学院专门针对小朋友研发的图形化编程语言 无需英文和代码基础
  • win7系统扩展双屏幕时,如何在两个屏幕下都显示任务栏

    扩展屏幕下都显示任务栏 win7系统本身无法设置该功能 目前我是不知道 但可以下载第三方软件来解决该问题 第一步 Dual Monitor Taskbar 下载软件 下载链接 http pan baidu com s 1o61isjw 密码
  • Web 浏览器演变史

    浏览器的演变是由梦想和创新编织而成的 Tim Bernas Lee 在80年代在CERN工作时 提出了HTML技术 用以改善CERN庞大的信息管理需求 Tim 也编写了第一款浏览器 它是基于NeXT提供的interface builder开
  • 【STM32学习笔记】(7)——STM32时钟系统详解

    STM32时钟系统 时钟系统的简介 RCC Reset Clock Control 复位和时钟控制器 时钟是单片机运行的基础 时钟信号推动单片机内各个部分执行相应的指令 时钟系统就是CPU的脉搏 决定cpu速率 像人的心跳一样 只有有了心跳
  • 深度优先搜索(DFS) 广度优先搜索(BFS)

    深度优先搜索算法 Depth First Search DFS是搜索算法的一种 它沿着树的深度遍历树的节点 尽可能深的搜索树的分支 当节点v的所有边都己被探寻过 搜索将回溯到发现节点v的那条边的起始节点 这一过程一直进行到已发现从源节点可达
  • 软件系统产品线特征及构建过程

    根据SEI定义 结合业界的一些研究 软件产品线有如下几个重要特征 1 一个软件产品线应该有一系列的产品成员组成 既产品家族 2 产品家族中的所有产品都服务于一些特定的领域 3 产品家族成员之间在服务功能 产品质量 产品性能 产品应用范围等方
  • Kotlin协程概览

    协程 Coroutines 并不是 Kotlin 提出来的新概念 很多的编程语言都有实现 如 Go Python 等 本文所讲 专指kotlin的协程 在Android 11中 Asynctask已经被废弃了 因为协程可以更简单 直观的实现