Android 上的协程(第一部分):了解背景
本系列文章
Android 上的协程(第一部分):了解背景
Android 上的协程(第二部分):入门
Android上的协程 (第三部分): 实际应用
Android 上的协程(第一部分):了解背景
这篇文章重点介绍协程的工作原理以及它们解决的问题。
协程解决什么问题?
Kotlin协程引入了一种新的并发方式,可以在 Android 上使用它来简化异步代码。虽然协程在 1.3 中是 Kotlin 的新概念,但协程的概念自编程语言诞生以来就一直存在。第一个探索使用协程的语言是1967 年的Simula。
在过去的几年中,协程越来越受欢迎,现在已包含在许多流行的编程语言中,例如Javascript、C#、Python、Ruby和Go等等。Kotlin 协程基于已用于构建大型应用程序的既定概念。
在 Android 上,协程可以很好地解决两个问题:
长时间运行的任务是花费太长时间阻塞主线程的任务。
Main-safety允许您确保可以从主线程调用任何挂起函数。
让我们深入了解协程,看看协程如何帮助我们以更简洁的方式构建代码!
长时间运行的任务
获取网页或与 API 交互都涉及发出网络请求。类似地,从数据库读取或从磁盘加载图像涉及读取文件。这些类型的事情就是我所说的长时间运行的任务——这些任务花费的时间太长以至于您的应用程序无法停止并等待它们!
与网络请求相比,现代手机执行代码的速度可能很难理解。在 Pixel 2 上,单个 CPU 周期仅需不到 0.0000000004 秒,这个数字很难用人类的术语来理解。但是,如果您将网络请求视为一眨眼,大约 400 毫秒(0.4 秒),就更容易理解 CPU 的运行速度。一眨眼的功夫,或者一个有点慢的网络请求,CPU 可以执行超过 10 亿个周期!
在 Android 上,每个应用程序都有一个主线程,负责处理 UI(如绘图视图)和协调用户交互。如果此线程上发生过多工作,应用程序似乎会挂起或变慢,从而导致不良的用户体验。任何长时间运行的任务都应该在不阻塞主线程的情况下完成,这样您的应用程序就不会显示所谓的“卡顿”,如冻结的动画,或者对触摸事件的响应缓慢。
为了在主线程之外执行网络请求,一个常见的模式是回调。回调提供了一个库的句柄,它可以用来在将来某个时间回调到您的代码中。使用回调的话,获取developer.android.com
上的数据可能是下面的样子:
class ViewModel: ViewModel() {fun fetchDocs() {get("developer.android.com") { result ->show(result)}}
}
即使get
是从主线程调用,它也会使用另一个线程来执行网络请求。然后,一旦网络上的结果可用,就会在主线程上调用回调。这是处理长时间运行任务的好方法,像Retrofit
这样的库可以帮助您在不阻塞主线程的情况下发出网络请求。
使用协同程序处理长时间运行的任务
协程是一种简化用于管理长时间运行任务(如fetchDocs. 为了探索协程如何使长时间运行的任务的代码更简单,让我们重写上面的回调示例以使用协程。
// Dispatchers.Main
suspend fun fetchDocs() {// Dispatchers.Mainval result = get("developer.android.com")// Dispatchers.Mainshow(result)
}
// look at this in the next section
suspend fun get(url: String) = withContext(Dispatchers.IO){/*...*/}
这段代码不会阻塞主线程吗?get
它如何从没有返回结果等待网络请求并阻塞?它事实证明,协程为 Kotlin 提供了一种执行此代码且从不阻塞主线程的方法。
协程通过添加两个新操作建立在常规函数之上。除了invoke(或 call)和return之外,协程还添加了suspend
和resume
。
- suspend — 暂停当前协程的执行,保存所有局部变量
- resume — 从暂停的地方继续暂停的协程
此功能由 Kotlin 通过函数上的 suspend 关键字添加。您只能从其他挂起函数调用挂起函数,或者使用协程构建器(如launch启动新协程)。
Suspend 和 Resume 一起工作来代替回调。
在上面的示例中,get将在启动网络请求之前暂停协程。该函数get仍将负责在主线程之外运行网络请求。然后,当网络请求完成时,它可以简单地恢复它挂起的协程,而不是调用回调来通知主线程。
查看如何fetchDocs执行,您可以了解suspend
的工作原理。每当协程挂起时,都会复制并保存当前堆栈帧(Kotlin 用来跟踪正在运行的函数及其变量的位置)。当它恢复时,堆栈帧从它保存的地方被复制回来并再次开始运行。在动画中间——当主线程上的所有协程都挂起时,主线程可以自由地更新屏幕和处理用户事件。suspend 和 resume 一起替换回调。很简约!
当主线程上的所有协程都挂起后,主线程就可以自由地做其他工作了。
即使我们编写了看起来完全像阻塞网络请求的简单顺序代码,协程也会按照我们的需要运行我们的代码并避免阻塞主线程!
接下来,让我们看看如何使用协程来实现主安全并探索调度程序。
协同程序的主要安全性
在 Kotlin 协程中,编写良好的挂起函数始终可以安全地从主线程调用。不管他们做什么,他们应该总是允许任何线程调用他们。
但是,我们在 Android 应用程序中做的很多事情都太慢而无法在主线程上发生。网络请求、解析 JSON、从数据库读取或写入,甚至只是遍历大型列表。其中任何一个都有可能运行缓慢到足以导致用户可见的“卡顿”,并且应该在主线程之外运行。
使用suspend
不会告诉 Kotlin 在后台线程上运行函数。值得清楚且经常说的是协程将在主线程上运行。事实上,在启动协程以响应 UI 事件时使用Dispatchers.Main.immediate
是一个非常好的主意——这样,如果您最终没有执行需要 main-safety 的长时间运行的任务,结果可以在下一帧中为用户提供。
协程会运行在主线程上,挂起不代表后台。
Default要使一个对于主线程来说工作速度太慢的函数是主安全的,您可以告诉 Kotlin 协程在 the或dispatcher上执行工作IO。在 Kotlin 中,所有协程都必须在调度程序中运行——即使它们在主线程上运行也是如此。协程可以自行暂停,而调度程序知道如何恢复它们。
为了指定协程应该在何处运行,Kotlin 提供了三个可用于线程分派的分派器。
+-----------------------------------+
| Dispatchers.Main |
+-----------------------------------+
| Main thread on Android, interact |
| with the UI and perform light |
| work |
+-----------------------------------+
| - Calling suspend functions |
| - Call UI functions |
| - Updating LiveData |
+-----------------------------------++-----------------------------------+
| Dispatchers.IO |
+-----------------------------------+
| Optimized for disk and network IO |
| off the main thread |
+-----------------------------------+
| - Database* |
| - Reading/writing files |
| - Networking** |
+-----------------------------------++-----------------------------------+
| Dispatchers.Default |
+-----------------------------------+
| Optimized for CPU intensive work |
| off the main thread |
+-----------------------------------+
| - Sorting a list |
| - Parsing JSON |
| - DiffUtils |
+-----------------------------------+
如果您使用挂起函数、RxJava或LiveData , Room将自动提供主安全。
Retrofit和Volley等网络库管理它们自己的线程,并且在与 Kotlin 协程一起使用时不需要在代码中显式维护主安全。
继续上面的示例,让我们使用调度程序来定义函数get。get在您调用的主体内withContext(Dispatchers.IO)
创建一个将在IO调度程序上运行的块。您放入该块内的任何代码将始终在调度程序上执行IO。由于withContext
它本身是一个挂起函数,它将使用协程来提供主要安全性。
// Dispatchers.Main
suspend fun fetchDocs() {// Dispatchers.Mainval result = get("developer.android.com")// Dispatchers.Mainshow(result)
}
// Dispatchers.Main
suspend fun get(url: String) =// Dispatchers.MainwithContext(Dispatchers.IO) {// Dispatchers.IO/* perform blocking network IO here */}// Dispatchers.Main
使用协程,您可以使用细粒度控制进行线程分派。因为withContext
允许您控制任何代码行在哪个线程上执行而无需引入回调来返回结果,所以您可以将它应用于非常小的功能,例如从数据库读取或执行网络请求。因此,一个好的做法是withContext
确保每个函数都可以安全地在任何Dispatcher包含上调用Main——这样调用者就不必考虑执行该函数需要什么线程。
在这个例子中,fetchDocs
在主线程上执行,但可以安全地调用get它在后台执行网络请求。因为协程支持suspend
和resume
,一旦块withContext
完成,主线程上的协程将恢复结果。
编写良好的挂起函数总是可以安全地从主线程(或主线程)调用。
使每个挂起函数都是主线程安全的是一个非常好的主意。如果它做任何涉及磁盘、网络的事情,甚至只是使用过多的 CPU,请使用withContext它来确保从主线程调用是安全的。这是 Retrofit 和 Room 等基于协程的库所遵循的模式。如果您在整个代码库中都遵循这种风格,您的代码将会更加简单,并且可以避免将线程问题与应用程序逻辑混在一起。如果始终遵循,协程可以在主线程上自由启动并使用简单的代码发出网络或数据库请求,同时保证用户不会看到“卡顿”。
withContext的表现
withContext
与提供主要安全性的回调或 RxJava 一样快。在某些情况下,甚至可以优化withContext
调用,使其超出回调所能达到的范围。如果一个函数将对数据库进行 10 次调用,您可以告诉 Kotlin 在所有 10 次调用周围切换一次withContext
。然后,即使数据库 library 会对withContext
重复调用,它也会留在同一个调度程序上并遵循快速路径。此外,对Dispatchers.Default
和之间的切换Dispatchers.IO
进行了优化,以尽可能避免线程切换。
在这篇文章中,我们探讨了协程最擅长解决的问题。协程是编程语言中一个非常古老的概念,最近变得流行,因为它们能够简化与网络交互的代码。
在 Android 上,您可以使用它们来解决两个非常常见的问题:
1.简化长时间运行任务的代码,例如从网络、磁盘读取,甚至解析大型 JSON 结果。
2.执行精确的 main-safety 以确保您不会意外阻塞主线程而不会使代码难以读写。
相关文章:

Android 上的协程(第一部分):了解背景
本系列文章 Android 上的协程(第一部分):了解背景 Android 上的协程(第二部分):入门 Android上的协程 (第三部分): 实际应用 Android 上的协程(第一部分):了解背景 这篇…...
【H3C】VRRP2 及Vrrp3基本原理 华为同用
文章目录VRRP2基本概念报文格式主备选举规则(优先级)0和255双Master原因VRRP认证VRRP状态机抢占模式VRRP主备切换状态项目场景VRRP3H3C参考致谢VRRP2 基本概念 VRRP路由器(VRRP Router):运行VRRP的设备,它…...

【数据库】SQL语法
目录 1. 常用数据类型 2. 约束 3. 数据库操作 4. 数据表操作 查看表 创建表格 添加数据 删除数据 修改数据 单表查询数据 多表查询数据 模糊查询 关联查询 连接查询 数据查询的执行顺序 4. 内置函数 1. 常用数据类型 整型:int浮点型:flo…...

JavaEE简单示例——文件的上传和下载
文件的上传和下载的实现原理的简单介绍 表单的构成 首先,我们先来介绍我们的需要用到的表单,在这个表单中,首先值得我们注意的就是,在type为file的input标签中.这个控件是我们主要用来选择上传的文件的, 除此之外,我们要想实现文件的上传,还需要将method的属性的值设置为post…...

【C语言督学训练营 第五天】数组字符串相关知识
文章目录前言一、数组的定义1.一维数组①.如何定义②.声明规则③.内存分布④.初始化方法2.二维数组3.高维数组二、访问数组元素相关问题1.访问越界2.数组的传递三、Scanf与字符数组1.字符数组初始化2.scanf读取字符四、字符数组相关函数前言 今天的C语言训练营没有安排高维数组…...

GPT-4 免费体验方法
POE 在Quora上非常受欢迎的手机聊天机器人Poe App已经集成ChatGPT助手!除了最初集成的三个聊天机器人Sage、Claude和Dragonfly外,Poe现在还加入了第四位ChatGPT。由于使用了ChatGPT API,因此Poe拥有真正的ChatGPT。 现在更是第一批集成了GP…...
中断-屏蔽位
1.中断控制器(PIC:适用于单处理器、APIC) 1.定义 中断控制器可以看作是中断服务的代理,外设五花八门,如果没有一个中断的代理,外设想要给cpu发送中断信号来处理中断。那么只能是外设连接在cpu引脚上,由于cpu引脚很宝贵,所以不可能拿出那么多引脚来供外设连接,所以就有…...
【洛谷P1636】 Einstein学画画
题目描述:Einstein 学起了画画。此人比较懒~~,他希望用最少的笔画画出一张画……给定一个无向图,包含 n 个顶点(编号 1∼n),m 条边,求最少用多少笔可以画出图中所有的边。输入格式第一行两个整数…...
户外LED显示屏钢结构制作原则
户外LED显示屏在施工安装时是必须要制作固定钢结构的,因为户外LED显示屏工作环境相对比较恶劣,制作钢结构一是为了安全,二是为了提高防护等级。那么户外LED显示屏钢结构制作原则是什么呢?迈普光彩小编总结了一些分享个大家。 户外…...
【内网穿透】使用Haproxy反向代理搭建企业私有云:神卓互联教程
神卓互联是一款强大的内网穿透工具,可以帮助企业搭建私有云,实现对内部资源的远程访问。在搭建私有云的过程中,使用HAProxy反向代理可以提高系统的性能和可靠性。本文将介绍如何使用神卓互联和HAProxy反向代理搭建私有云。 步骤如下…...
spring boot项目:实现与数据库的连接
步骤【写在前面】定义数据库连接信息:引入数据库驱动:创建数据源:创建JdbcTemplate:编写DAO层:使用Service注解标注Service层:使用RestController注解标注Controller层:示例代码:app…...

【gitlab部署】centos8安装gitlab(搭建属于自己的代码服务器)
这里写目录标题部署篇序言要求检查系统是否安装OpenSSH防火墙问题准备gitlab.rb 配置坑点一忘记root密码重置使用篇gitlab转换成中文git关闭注册入口创建用户部署篇 序言 在团队开发过程中,想要拥有高效的开发效率,选择一个好的代码开发工具是必不可少的…...
2021年全国职业院校技能大赛(中职组)网络安全竞赛第三套试题A模块解析(超级详细)
2021年全国职业院校技能大赛(中职组) 网络安全竞赛试题 (3) (总分100分) 赛题说明 一、竞赛项目简介 “网络安全”竞赛共分A. 基础设施设置与安全加固;B. 网络安全事件响应、数字取证调查和应用安全;C. CTF夺旗-攻击;D. CTF夺旗-防御等四个模块。根据比赛实际情况…...

Hbase异步复制和同步复制解析
背景 Hbase是一个KV数据库,自然和Mysql以及Redis等会涉及到复制的问题,也有主从集群的概念,那么本文就来看下Hbase的复制逻辑 Hbase复制实现 首先我们先在回顾下,在Hbase实现中,每个RegionServer上面会包含多个Regi…...

TIKTOK海外直播公会如何申
在“清朗行动”的规范化整治下,国内秀场直播俨然成为了“夕阳行业”,早已度过了野蛮生长的阶段。随着直播公会内卷竞争加剧,公会的生存也愈发艰难,有的娱乐主播甚至纷纷转行做起了电商,可见国内娱乐直播行业的惨淡。 …...

6.springcloud微服务架构搭建 之 《springboot集成Gateway》
5.springcloud微服务架构搭建 之 《springboot集成Hystrix》 目录 1.gateway介绍 2.项目引入gateway 3.yml配置gateway参数 5.自定义全局Filter 6.测试 1.gateway介绍 服务网关(Spring Cloud Gateway)是Spring Cloud官方推出的 第二代网关框架&#…...

[N1CTF 2018]eating_cms_
目录 信息收集 代码审计 parse_url解析漏洞 信息收集 进入即是登录页面,抓包一看应该是SQL注入,但是空格、%、|等等啥的都被waf了,不太好注入,先信息收集一波 花一分钟扫下目录,发现一个viminfo和register.php Viminfo文件…...

《Spring系列》第13章 Aop切面(二) 代理创建
前言 本篇文章主要介绍AOP的源码,要想看懂AOP,那么就看AOP给容器中注入了什么组件,这个组件什么时候工作,这个组件的功能是什么? EnableAspectJAutoProxy会向IOC容器中注入一个后置处理器,它会在Bean的创…...
算法-贪心
贪心算法1信息学竞赛课堂贪心算法2贪心法实际生活中,经常需要求一些问题的“可行解”和“最优解”,这就是所谓的“最优化”问题。一般来说,每个最优化问题都包含一组“限制条件”和一个“目标函数”,符合限制条件的问题求解方案称…...

【数据结构与算法】树(Tree)【详解】
文章目录前言树一、树的基本概念1、树的定义2、基本术语3、树的性质二、树的存储结构1、双亲表示法2、孩子表示法3、孩子兄弟表示法二叉树一、二叉树的概念1、二叉树的定义2、几个特殊的二叉树3、二叉树的性质4、二叉树的存储结构二、遍历二叉树1、先序遍历2、中序遍历3、后序遍…...

SpringBoot+uniapp 的 Champion 俱乐部微信小程序设计与实现,论文初版实现
摘要 本论文旨在设计并实现基于 SpringBoot 和 uniapp 的 Champion 俱乐部微信小程序,以满足俱乐部线上活动推广、会员管理、社交互动等需求。通过 SpringBoot 搭建后端服务,提供稳定高效的数据处理与业务逻辑支持;利用 uniapp 实现跨平台前…...
OpenLayers 分屏对比(地图联动)
注:当前使用的是 ol 5.3.0 版本,天地图使用的key请到天地图官网申请,并替换为自己的key 地图分屏对比在WebGIS开发中是很常见的功能,和卷帘图层不一样的是,分屏对比是在各个地图中添加相同或者不同的图层进行对比查看。…...

使用 SymPy 进行向量和矩阵的高级操作
在科学计算和工程领域,向量和矩阵操作是解决问题的核心技能之一。Python 的 SymPy 库提供了强大的符号计算功能,能够高效地处理向量和矩阵的各种操作。本文将深入探讨如何使用 SymPy 进行向量和矩阵的创建、合并以及维度拓展等操作,并通过具体…...
iOS性能调优实战:借助克魔(KeyMob)与常用工具深度洞察App瓶颈
在日常iOS开发过程中,性能问题往往是最令人头疼的一类Bug。尤其是在App上线前的压测阶段或是处理用户反馈的高发期,开发者往往需要面对卡顿、崩溃、能耗异常、日志混乱等一系列问题。这些问题表面上看似偶发,但背后往往隐藏着系统资源调度不当…...

CVE-2020-17519源码分析与漏洞复现(Flink 任意文件读取)
漏洞概览 漏洞名称:Apache Flink REST API 任意文件读取漏洞CVE编号:CVE-2020-17519CVSS评分:7.5影响版本:Apache Flink 1.11.0、1.11.1、1.11.2修复版本:≥ 1.11.3 或 ≥ 1.12.0漏洞类型:路径遍历&#x…...
Go 并发编程基础:通道(Channel)的使用
在 Go 中,Channel 是 Goroutine 之间通信的核心机制。它提供了一个线程安全的通信方式,用于在多个 Goroutine 之间传递数据,从而实现高效的并发编程。 本章将介绍 Channel 的基本概念、用法、缓冲、关闭机制以及 select 的使用。 一、Channel…...
写一个shell脚本,把局域网内,把能ping通的IP和不能ping通的IP分类,并保存到两个文本文件里
写一个shell脚本,把局域网内,把能ping通的IP和不能ping通的IP分类,并保存到两个文本文件里 脚本1 #!/bin/bash #定义变量 ip10.1.1 #循环去ping主机的IP for ((i1;i<10;i)) doping -c1 $ip.$i &>/dev/null[ $? -eq 0 ] &&am…...

五、jmeter脚本参数化
目录 1、脚本参数化 1.1 用户定义的变量 1.1.1 添加及引用方式 1.1.2 测试得出用户定义变量的特点 1.2 用户参数 1.2.1 概念 1.2.2 位置不同效果不同 1.2.3、用户参数的勾选框 - 每次迭代更新一次 总结用户定义的变量、用户参数 1.3 csv数据文件参数化 1、脚本参数化 …...

欢乐熊大话蓝牙知识17:多连接 BLE 怎么设计服务不会乱?分层思维来救场!
多连接 BLE 怎么设计服务不会乱?分层思维来救场! 作者按: 你是不是也遇到过 BLE 多连接时,调试现场像网吧“掉线风暴”? 温度传感器连上了,心率带丢了;一边 OTA 更新,一边通知卡壳。…...

【多线程初阶】单例模式 指令重排序问题
文章目录 1.单例模式1)饿汉模式2)懒汉模式①.单线程版本②.多线程版本 2.分析单例模式里的线程安全问题1)饿汉模式2)懒汉模式懒汉模式是如何出现线程安全问题的 3.解决问题进一步优化加锁导致的执行效率优化预防内存可见性问题 4.解决指令重排序问题 1.单例模式 单例模式确保某…...