当前位置: 首页 > news >正文

Android 上的协程(第一部分):了解背景

本系列文章

Android 上的协程(第一部分):了解背景
Android 上的协程(第二部分):入门
Android上的协程 (第三部分): 实际应用

Android 上的协程(第一部分):了解背景

kotlin-logo
这篇文章重点介绍协程的工作原理以及它们解决的问题。

协程解决什么问题?

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之外,协程还添加了suspendresume

  • suspend — 暂停当前协程的执行,保存所有局部变量
  • resume — 从暂停的地方继续暂停的协程
    此功能由 Kotlin 通过函数上的 suspend 关键字添加。您只能从其他挂起函数调用挂起函数,或者使用协程构建器(如launch启动新协程)。

Suspend 和 Resume 一起工作来代替回调。

在上面的示例中,get将在启动网络请求之前暂停协程。该函数get仍将负责在主线程之外运行网络请求。然后,当网络请求完成时,它可以简单地恢复它挂起的协程,而不是调用回调来通知主线程。

展示 Kotlin 如何实现挂起和恢复以替换回调的动画
查看如何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它在后台执行网络请求。因为协程支持suspendresume,一旦块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反向代理搭建私有云。 步骤如下&#xf…...

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、后序遍…...

使用VSCode开发Django指南

使用VSCode开发Django指南 一、概述 Django 是一个高级 Python 框架,专为快速、安全和可扩展的 Web 开发而设计。Django 包含对 URL 路由、页面模板和数据处理的丰富支持。 本文将创建一个简单的 Django 应用,其中包含三个使用通用基本模板的页面。在此…...

springboot 百货中心供应链管理系统小程序

一、前言 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,百货中心供应链管理系统被用户普遍使用,为方…...

调用支付宝接口响应40004 SYSTEM_ERROR问题排查

在对接支付宝API的时候,遇到了一些问题,记录一下排查过程。 Body:{"datadigital_fincloud_generalsaas_face_certify_initialize_response":{"msg":"Business Failed","code":"40004","sub_msg…...

《Qt C++ 与 OpenCV:解锁视频播放程序设计的奥秘》

引言:探索视频播放程序设计之旅 在当今数字化时代,多媒体应用已渗透到我们生活的方方面面,从日常的视频娱乐到专业的视频监控、视频会议系统,视频播放程序作为多媒体应用的核心组成部分,扮演着至关重要的角色。无论是在个人电脑、移动设备还是智能电视等平台上,用户都期望…...

理解 MCP 工作流:使用 Ollama 和 LangChain 构建本地 MCP 客户端

🌟 什么是 MCP? 模型控制协议 (MCP) 是一种创新的协议,旨在无缝连接 AI 模型与应用程序。 MCP 是一个开源协议,它标准化了我们的 LLM 应用程序连接所需工具和数据源并与之协作的方式。 可以把它想象成你的 AI 模型 和想要使用它…...

Linux云原生安全:零信任架构与机密计算

Linux云原生安全:零信任架构与机密计算 构建坚不可摧的云原生防御体系 引言:云原生安全的范式革命 随着云原生技术的普及,安全边界正在从传统的网络边界向工作负载内部转移。Gartner预测,到2025年,零信任架构将成为超…...

【Java_EE】Spring MVC

目录 Spring Web MVC ​编辑注解 RestController RequestMapping RequestParam RequestParam RequestBody PathVariable RequestPart 参数传递 注意事项 ​编辑参数重命名 RequestParam ​编辑​编辑传递集合 RequestParam 传递JSON数据 ​编辑RequestBody ​…...

GC1808高性能24位立体声音频ADC芯片解析

1. 芯片概述 GC1808是一款24位立体声音频模数转换器(ADC),支持8kHz~96kHz采样率,集成Δ-Σ调制器、数字抗混叠滤波器和高通滤波器,适用于高保真音频采集场景。 2. 核心特性 高精度:24位分辨率&#xff0c…...

Pinocchio 库详解及其在足式机器人上的应用

Pinocchio 库详解及其在足式机器人上的应用 Pinocchio (Pinocchio is not only a nose) 是一个开源的 C 库,专门用于快速计算机器人模型的正向运动学、逆向运动学、雅可比矩阵、动力学和动力学导数。它主要关注效率和准确性,并提供了一个通用的框架&…...

用机器学习破解新能源领域的“弃风”难题

音乐发烧友深有体会,玩音乐的本质就是玩电网。火电声音偏暖,水电偏冷,风电偏空旷。至于太阳能发的电,则略显朦胧和单薄。 不知你是否有感觉,近两年家里的音响声音越来越冷,听起来越来越单薄? —…...