Kotlin协程的简单用法(GlobalScope、lifecycleScope、viewModelScope)

2023-10-26

协程(Coroutine)

  • 协程就像非常轻量级的线程。线程是由系统调度的,线程切换或线程阻塞的开销都比较大。而协程依赖于线程,但是协程挂起时不需要阻塞线程,协程是由开发者控制的。所以协程也像用户态的线程,非常轻量级,一个线程中可以创建任意个协程。
  • 协程就像轻量级的线程。线程由系统调度,协程由开发者控制。
  • kotlin协程本质上是对线程池的封装
    协程通过将线程切换的复杂性封装入库来简化异步编程。程序的逻辑可以在协程中顺序地表达,而底层库会为我们解决其异步性。

GlobalScope(不推荐)

  • GlobalScope.launch
    使用的是DefaultDispatcher,会自动切换到后台线程,不能做UI操作
        GlobalScope.launch {
        	//GlobalScope开启协程:DefaultDispatcher-worker-1
         	Log.d(TAG, "GlobalScope开启协程:" + Thread.currentThread().name)
			//子线程中此处不可以做UI操作
			//Toast.makeText(this@MainActivity, "GlobalScope开启协程", Toast.LENGTH_SHORT).show()
           
        }
  • 可以在协程中切换线程
        GlobalScope.launch {
        	//GlobalScope开启协程:DefaultDispatcher-worker-1
         	Log.d(TAG, "GlobalScope开启协程:" + Thread.currentThread().name)
			//子线程中此处不可以做UI操作
			//Toast.makeText(this@MainActivity, "GlobalScope开启协程", Toast.LENGTH_SHORT).show()
			withContext(Dispatchers.Main){
                Toast.makeText(this@MainActivity, "协程中切换线程", Toast.LENGTH_SHORT).show()
            }
        }
  • GlobalScope.launch(Dispatchers.Main)
    通过Dispatchers.Main使协程依托于主线程中,此时可以更新UI等操作
        GlobalScope.launch(Dispatchers.Main) {
        	//GlobalScope开启协程:main
            Log.d(TAG, "GlobalScope开启协程:" + Thread.currentThread().name)
            //可以做UI操作
            Toast.makeText(this@MainActivity, "GlobalScope开启协程", Toast.LENGTH_SHORT).show()
        }

lifecycleScope、viewModelScope(推荐)

  • 引入方式
    implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.2.0'//lifecycleScope
    implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0'//viewModelScope
  • GlobalScope是生命周期是process级别的,即使Activity或Fragment已经被销毁,协程仍然在执行。所以需要绑定生命周期。
  • lifecycleScope只能在Activity、Fragment中使用,会绑定Activity和Fragment的生命周期
  • viewModelScope只能在ViewModel中使用,绑定ViewModel的生命周期

协程的执行顺序

    private fun test() {
        Log.d(TAG, "test: 方法开始")
        lifecycleScope.launch {
            delay(1000)
            Log.d(TAG, "test: " + Thread.currentThread().name)
            Log.d(TAG, "test: 协程结束")
            Toast.makeText(this@MainActivity, "协程结束", Toast.LENGTH_SHORT).show()
        }
        Log.d(TAG, "test: 方法结束")
    }
D/MainActivity: test: 方法开始
D/MainActivity: test: 方法结束
D/MainActivity: test: main
D/MainActivity: test: 协程结束
  • 协程内的阻塞不会影响协程外
  • 由打印结果可以看出协程体是异步执行的,但是可以在其中做UI操作。线程也是异步的,但是不能更新UI,线程需要先切换到主线程。

协程中多个耗时任务的串行

  • 默认情况下协程中的内容是串行的
    private fun test2() {
        lifecycleScope.launch {
            val startTime = System.currentTimeMillis()
            val a = getDataA()
            val b = getDataB()
            val sum = a + b
            //D/MainActivity: test2: sum = 3,耗时:3008
            Log.d(TAG, "test2: sum = $sum,耗时:${System.currentTimeMillis() - startTime}")
        }
    }

    private suspend fun getDataA(): Int {
        delay(1000)
        return 1
    }

    private suspend fun getDataB(): Int {
        delay(2000)
        return 2
    }
D/MainActivity: test2: sum = 3,耗时:3008

协程中多个耗时任务的并行

  • 如果需要并行,例如请求多个接口拿到数据后才能进行操作
    private fun test3(){
        lifecycleScope.launch {
            val startTime = System.currentTimeMillis()
            val a = lifecycleScope.async { getDataA() }
            val b = lifecycleScope.async { getDataB() }
            val sum = a.await() + b.await()
            //D/MainActivity: test3: sum = 3,耗时:2009
            Log.d(TAG, "test3: sum = $sum,耗时:${System.currentTimeMillis() - startTime}")
        }
    }
    
    private suspend fun getDataA(): Int {
        delay(1000)
        return 1
    }

    private suspend fun getDataB(): Int {
        delay(2000)
        return 2
    }

D/MainActivity: test3: sum = 3,耗时:2009

协程的停止

  • 手动停止的情况 job?.cancel()
    private var job: Job? = null

    private fun test4() {
        job = lifecycleScope.launch {
            ...
        }
        job?.cancel()
    }
  • lifecycleScope和viewModelScope会绑定调用者的生命周期,因此通常情况下不需要手动去停止

suspend协程挂起原理

  • 在编译期,将suspend标记的方法转化成接口回调的方式,本质上还是基于回调实现的。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Kotlin协程的简单用法(GlobalScope、lifecycleScope、viewModelScope) 的相关文章

随机推荐

  • unity第一人称射击游戏,枪击游戏,功能完整可以当大作业或者毕设

    unity第一人称射击游戏 枪击游戏 含源码和exe导出文件 下载链接在文末 unity第一人称枪击游戏 分为海岛地图和沙漠地图 可以开镜射击 敌人可以移动 菜单页面有开始游戏 游戏说明 退出三个按钮 游戏有音效 可以通过ASDW移动人物
  • 查看jks证书内容

    keytool list v keystore xxx jks 输入密钥库口令
  • C++进阶必读书籍

    结合一些我的学习经历 希望对于想学C 的人有些帮助 大家有什么好想法望提出 我老师最初是从C语言教起的 用的是潭浩强的 lt
  • ajax day3

    3 将普通对象转为查询参数字符串形式 创建URLSearchParams参数 再用toString方法转为字符串 4 xhr对象 请求参数 body参数 5 promise promise对象一旦被兑现或拒绝 就是已敲定了 状态无法再被改变
  • Python模块之操作数据库MySQL篇

    目录 一 安装PyMySQL模块 二 操作数据库 1 连接数据库 2 执行sql语句 execute和executemany 3 创建数据表 三 操作MySQL数据表 1 新增数据 2 查询数据 3 修改数据 4 删除数据 5 踩到的坑 一
  • python批量读取Excel文件

    将同一个文件夹下的xlsx文件读取 import os import pandas as pd path r path of file for i in os listdir path df pd read excel os path jo
  • [读论文]CAAD-2018 Targeted Attack方向季军技术报告

    这次分享的是CAAD 2018比赛中Northwest Security团队的技术报告 该团队在此次比赛中取得了了targeted Attack 方向第三名 non targeted Attack方向第四名的成绩 题目 Leverage O
  • 构建 fluentd 镜像与部署应用

    本文将具体介绍如何在基础镜像 ubuntu 20 04 上搭建 fluentd 镜像 并且实现监控指定目录的日志文件 构建镜像 首先 从 docker hub 中挑选一个合适的基础镜像 例如 ubuntu 20 04 docker pull
  • 区块链+物联网=?

    链客 专为开发者而生 有问必答 此文章来自区块链技术社区 未经允许拒绝转载 区块链与物联网 IoT 的交叉应用已成为最有前途的区块链用例之一 在过去的几个月里 IoTeX一直与我们的战略合作伙伴合作 并进行了独立的研究 为了能够在短期内采用
  • 新闻

    4月 中国科技产业智库甲子光年发布 AIGC应用与实践研究展望报告 及AIGC产业图谱 面向AIGC技术创新者 产业参与者 资本机构和政府等各方展现AIGC产业的整体生态环境和行业发展 华院数智人凭借其在生成式AI技术 人机交互能力和市场应
  • esp32+vscode环境搭建速记

    esp32idf vscode环境搭建速记 建议按照入下步骤进行 在vscode插件里安装esp32idf 或者用在线的下载器安装会出现一些莫名奇妙的问题 第一步 安装esp32idf 官方网址 https dl espressif cn
  • gridlayout java_Swing-布局管理器之GridLayout(网格布局)-入门

    网格布局特点 l 使容器中的各组件呈M行 N列的网格状分布 l 网格每列宽度相同 等于容器的宽度除以网格的列数 l 网格每行高度相同 等于容器的高度除以网格的行数 l 各组件的排列方式为 从上到下 从左到右 l 组件放入容器的次序决定了它在
  • idea中thymeleaf语法不提示的所有原因

    首先pom xml里面要导入thymeleaf的依赖 然后在html中加入 xmlns th http www thymeleaf org 最后点击file gt settings 查看插件是否使用 未使用点击打勾重启
  • AMS1117典型电路

    AMS1117 3 3V 5V 封装 常见应用连接 1 输入旁路电容Input Bypass Capacitor A 10uF tantalum on the input is a suitable input bypassing fora
  • 解决 SyntaxError:Unexpected end of JSON input 或 Unexpected token u in JSON at position 0 问题

    1 报错原因 JSON 接收的数据不完整 或者数据格式不符合要求 如 undefined 2 JSON 数据格式要求 1 JSON文件都是被包裹在一个大括号中 通过key value的方式来表达数据 2 JSON的Key必须包裹在一个双引号
  • Python 魔法方法(三) __getattr__,__setattr__, __delattr__

    1 getattr 当我们访问一个不存在的属性的时候 会抛出异常 提示我们不存在这个属性 而这个异常就是 getattr 方法抛出的 其原因在于他是访问一个不存在的属性的最后落脚点 作为异常抛出的地方提示出错再适合不过了 看例子 我们找一个
  • 调试最长的一帧(第27天)

    对于几个多线程渲染中的成员变量 继续抄一抄 Block阻塞器 BlockCount 计数器类 它与阻塞器类的使用方法基本相同 block 阻塞线程 release 释放线程 不过除此之外 BlockCount的构造函数还可以设置一个阻塞计数
  • Point Cloud Library学习之ICP迭代最近点匹配法NDT2D正态分布转换法

    参考来源 https pointclouds org documentation classpcl 1 1 registration html ab1d64f86162b2df716ead8d978579c11 http epsilonjo
  • ctab提取dna流程图_核酸提取、纯化与常见问题解答

    写在前面 核酸提取包括DNA提取 RNA提取 质粒提取 核酸是遗传信息的携带者 是基因表达的物质基础 是分子生物学研究的主要对象 无论是进行核酸的结构还是功能研究 首先都需要对核酸进行提取和纯化 核酸是生命的最基本物质之一 可分为DNA和R
  • Kotlin协程的简单用法(GlobalScope、lifecycleScope、viewModelScope)

    协程 Coroutine 协程就像非常轻量级的线程 线程是由系统调度的 线程切换或线程阻塞的开销都比较大 而协程依赖于线程 但是协程挂起时不需要阻塞线程 协程是由开发者控制的 所以协程也像用户态的线程 非常轻量级 一个线程中可以创建任意个协