suspend、suspendCancellableCoroutine、continuation-kotlin协程

0.首先整理协程的启动方法

在 Kotlin 中,启动协程的方法有多种,常用的方法包括 launchasync、和 runBlocking。每种方法有不同的用途和特性。以下是它们的详细介绍:

1. launch

launch 是一个启动新协程的方法,返回一个 Job 对象,表示协程的工作。它适用于启动不需要返回结果的协程。

import kotlinx.coroutines.*fun main() = runBlocking {// 启动一个新的协程val job = launch {delay(1000L) // 模拟长时间运行的任务println("Hello from launch!")}println("Waiting for job...")job.join() // 等待协程完成println("Job completed!")
}
输出示例
Waiting for job...
Hello from launch!
Job completed!

2. async

async 方法也用于启动新协程,但它返回一个 Deferred 对象,可以用来获取协程的结果。适用于需要返回结果的异步操作。

import kotlinx.coroutines.*fun main() = runBlocking {// 启动一个新的协程并返回结果val deferred = async {delay(1000L) // 模拟长时间运行的任务"Result from async"}println("Waiting for result...")val result = deferred.await() // 获取协程的结果println("Result: $result")
}
输出示例
Waiting for result...
Result: Result from async

3. runBlocking

runBlocking 是一个用于启动协程的特殊函数,它会阻塞当前线程,直到协程完成。通常用于主函数和测试环境。

import kotlinx.coroutines.*fun main() = runBlocking {// 启动协程println("Starting...")// 在 runBlocking 中启动新的协程launch {delay(1000L)println("Hello from runBlocking!")}// runBlocking 会等待所有子协程完成delay(2000L)println("Done!")
}
输出示例
Starting...
Hello from runBlocking!
Done!

4. withContext

withContext 用于切换到不同的协程上下文。它是一个挂起函数,适用于需要在不同上下文中执行代码的场景,比如在 IO 线程和主线程之间切换。

import kotlinx.coroutines.*fun main() = runBlocking {println("Main thread: ${Thread.currentThread().name}")val result = withContext(Dispatchers.IO) {// 切换到 IO 线程println("IO thread: ${Thread.currentThread().name}")delay(1000L) // 模拟耗时操作"Result from IO"}println("Result: $result")
}
输出示例
Main thread: main
IO thread: DefaultDispatcher-worker-1
Result: Result from IO

5. 自定义协程上下文

你也可以自定义协程上下文,例如使用 CoroutineScopeJob。这是在大型应用程序中管理协程的好方法。

import kotlinx.coroutines.*fun main() {val job = Job() // 创建 Jobval scope = CoroutineScope(Dispatchers.Main + job) // 创建 CoroutineScopescope.launch {delay(1000L)println("Hello from custom scope!")}// 取消所有协程job.cancel()
}

输出示例

 

总结

  • launch:用于启动不需要返回结果的协程。
  • async:用于启动需要返回结果的协程。
  • runBlocking:用于在主函数或测试中阻塞当前线程并等待协程完成。
  • withContext:用于在不同的协程上下文中切换。
  • 自定义协程上下文:通过 CoroutineScopeJob 管理协程的生命周期。

这些方法可以根据需求灵活选择,以便更好地管理异步操作和并发任务。

@RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)suspend fun connect() = suspendCancellableCoroutine<Boolean> { continuation ->scope.launch {if (getState() != State.DISCONNECTED) {continuation.resumeWithException(IllegalStateException("Invalid state for connect operation: ${getState()}"))return@launch}setState(State.CONNECTING)continuation.invokeOnCancellation {bluetoothGatt?.disconnect()}bluetoothGatt = device.device.connectGatt(context, false, gattCallback {Log.v(LOG_TAG, "Device connected ${device.manufacturerData.serialNumber}")continuation.resume(true)})}}

代码是一个挂起函数 connect 的实现,使用了 suspendCancellableCoroutine 来处理 Bluetooth GATT(通用属性配置文件)设备的连接操作。

1、@RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)

这是一个注解,表示该函数需要 BLUETOOTH_CONNECT 权限。这是 Android 中的一种权限管理方式,确保在调用此函数时,应用程序已经获得了适当的权限。

2、suspend fun connect()

声明了一个挂起函数 connect,意味着可以在协程中调用。

挂起函数不会阻塞线程,而是可以在需要时挂起执行,允许其他协程运行。当条件满足时,挂起的函数会继续执行。

import kotlinx.coroutines.*suspend fun fetchData(): String {delay(2000L) // 模拟网络请求的延迟return "Data fetched"
}fun main() = runBlocking {println("Fetching data...")val result = fetchData() // 调用挂起函数println(result) // 打印结果
}
Fetching data...
// 等待 2 秒
Data fetched

3、suspendCancellableCoroutine<Boolean> { continuation -> ... }

使用 suspendCancellableCoroutine 创建一个可以挂起的协程,同时允许在连接操作期间取消。

continuation 是用于恢复协程执行的对象。

4、scope.launch { ... }

在一个新的协程作用域中启动异步任务。

5、状态检查

if (getState() != State.DISCONNECTED) {continuation.resumeWithException(IllegalStateException("Invalid state for connect operation: ${getState()}"))return@launch
}

该部分检查设备的当前状态。如果设备未处于 DISCONNECTED 状态,则抛出异常并恢复协程的执行,返回错误信息。

6、设置连接状态

setState(State.CONNECTING)

将设备状态设置为 CONNECTING,指示正在进行连接操作。

7、取消处理

continuation.invokeOnCancellation {bluetoothGatt?.disconnect()
}

如果协程被取消,执行此代码以断开与设备的连接。

8、连接 GATT

bluetoothGatt = device.device.connectGatt(context, false, gattCallback { ... })

使用 connectGatt 方法连接到 Bluetooth GATT 设备。这里的 gattCallback 是一个回调函数,用于处理连接结果。

9、连接成功处理

Log.v(LOG_TAG, "Device connected ${device.manufacturerData.serialNumber}")
continuation.resume(true)

连接成功后,记录日志并通过 continuation.resume(true) 恢复协程,表示连接操作成功

  • 整体流程: 该函数通过检查设备状态,尝试连接到 Bluetooth GATT 设备,处理连接期间的取消,以及连接成功后的操作。
  • 可读性与可维护性: 使用挂起函数和协程提高了代码的可读性和可维护性,避免了回调地狱的问题。
  • 权限管理: 通过 @RequiresPermission 注解确保在进行 Bluetooth 操作之前,应用程序具有必要的权限,这提高了代码的安全性。

错误处理与扩展

  • 错误处理: 可以扩展对 connectGatt 调用的错误处理,以便在连接失败时执行某些逻辑。
  • 状态管理: 对 State 的管理可以进一步优化,确保在不同状态之间转换时的健壮性。

在 Kotlin 协程中,continuation 是一个重要的概念,它允许你控制协程的执行流。具体来说,continuation 表示协程的执行上下文,可以在异步操作完成时恢复协程的执行。

continuation 的基本功能

  1. 恢复协程: continuation 提供了两个主要方法,用于恢复协程的执行:

    • resume(value: T):用于恢复协程并返回一个值,表示操作成功。
    • resumeWithException(exception: Throwable):用于恢复协程并抛出一个异常,表示操作失败。
  2. 可取消性: continuation 是可取消的,这意味着如果协程在执行过程中被取消,可以在取消时处理特定逻辑。

import kotlinx.coroutines.*
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithExceptionsuspend fun fetchData() = suspendCancellableCoroutine<String> { continuation ->// 模拟异步操作,例如网络请求// 使用 CoroutineScope 启动一个新的协程GlobalScope.launch {try {// 模拟成功获取数据val data = "Fetched Data"// 恢复协程并返回数据continuation.resume(data)} catch (e: Exception) {// 恢复协程并抛出异常continuation.resumeWithException(e)}}// 处理取消continuation.invokeOnCancellation {// 执行清理工作,例如取消网络请求println("Operation was cancelled")}
}fun main() = runBlocking {try {val result = fetchData()println("Result: $result")} catch (e: Exception) {println("Error: ${e.message}")}
}

关键点

  1. 异步操作: continuation 允许你将异步操作与协程结合,使得代码看起来更加线性和易于理解。
  2. 错误处理: 通过 resumeWithException 可以优雅地处理错误,使得调用方能够捕获和处理异常。
  3. 取消处理: 使用 invokeOnCancellation 可以在协程被取消时执行清理操作,例如释放资源或中止网络请求。

应用场景

  • 异步编程: continuation 通常用于处理需要等待结果的异步操作,例如网络请求、数据库查询等。
  • 资源管理: 在涉及资源(如文件、网络连接等)的操作时,可以使用 continuation 来确保在取消或出错时正确释放这些资源。
  • 库开发: 当你在开发库或框架时,可以使用 continuation 来提供更灵活的异步操作支持,使得调用者可以轻松使用你的 API。

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

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

相关文章

uniapp生成随机数

推荐学习文档 golang应用级os框架&#xff0c;欢迎stargolang应用级os框架使用案例&#xff0c;欢迎star案例&#xff1a;基于golang开发的一款超有个性的旅游计划app经历golang实战大纲golang优秀开发常用开源库汇总想学习更多golang知识&#xff0c;这里有免费的golang学习笔…

深入理解C++ STL中的 vector

文章目录 1. vector 的概述1.1 vector 是什么&#xff1f;1.2 vector 的优点1.3 vector 的缺点 2. vector 的基本使用2.1 vector 的定义2.2 基本操作2.3 示例2.4 迭代器的使用 3. vector 的内部实现原理3.1 动态数组的实现3.2 内存管理3.3 内存扩展策略3.4 元素的插入与删除3.4…

VSCode 查看 Git 的历史记录的三种技巧

前言 在我们日常开发工作过程中&#xff0c;可能经常会看到一些离谱的历史代码&#xff0c;或者当项目发生线上事故时&#xff0c;如何快速定位是谁提交的代码导致的&#xff1f; 作为前端开发者&#xff0c;VSCode 是目前最为流行的代码编辑工具&#xff0c;也是日常最常打开…

服务器CUDA版本升级

https://blog.csdn.net/m0_52583356/article/details/138150039 上面这篇文章是按照显卡驱动所支持的最高cuda版本来更新cuda Toolkit的&#xff0c;但是如果你想要更新显卡驱动最高支持的CUDA版本&#xff0c;就需要更新显卡驱动了。 更新显卡驱动需要先卸载原有显卡驱动&am…

Vue中计算属性computed—(详解计算属性vs方法Methods,包括案例+代码)

文章目录 计算属性computed3.1 概述3.2 使用3.3 计算属性vs方法Methods3.4 计算属性的完整写法 计算属性computed 3.1 概述 基于现有的数据&#xff0c;计算出来的新属性。 依赖的数据变化&#xff0c;自动重新计算 语法&#xff1a; 声明在 computed 配置项中&#xff0c;…

OPC UA与PostgreSQL如何实现无缝连接?

随着工业4.0的推进&#xff0c;数据交换和集成在智能制造中扮演着越来越重要的角色。OPC UA能够实现设备与设备、设备与系统之间的高效数据交换。而PostgreSQL则是一种强大的开源关系型数据库管理系统&#xff0c;广泛应用于数据存储和管理。如何将OPC UA与PostgreSQL结合起来&…

python pip安装requirements.txt依赖与国内镜像

python pip安装requirements.txt依赖与国内镜像 如果网络通畅&#xff0c;直接pip安装依赖&#xff1a; pip install -r requirements.txt 如果需要国内的镜像&#xff0c;可以考虑使用阿里的&#xff0c;在后面加上&#xff1a; -i http://mirrors.aliyun.com/pypi/simple --…

基于System.js的微前端实现(插件化)

目录​​​​​​​ 写在前面 一、微前端相关知识 &#xff08;一&#xff09;概念 &#xff08;二&#xff09; 优势 &#xff08;三&#xff09; 缺点 &#xff08;四&#xff09;应用场景 &#xff08;五&#xff09;现有框架 1. qiankun 2. single-spa 3. SystemJ…

GO之流程控制

一、流程控制简述 一&#xff09;流程控制的作用 流程控制语句是用来控制程序中语句执行顺序的语句&#xff0c;可以把语句组合成能完成一定功能的小逻辑块 二&#xff09;流程控制的分类 控制语句分为三类&#xff1a;顺序、选择和循环 顺序结构&#xff1a;依次执行&#xf…

通过Express + Vue3从零构建一个用户认证与授权系统(二)数据库与后端项目搭建与实现

前言 上一篇完成了系统的相关设计文档的编写&#xff0c;本文将详细介绍如何一步步使用 TypeScript 和 Express 搭建一个模块化、类型安全的用户认证与授权系统&#xff0c;包括数据库设计、后端项目搭建、用户认证、角色与权限管理、错误处理以及 Swagger 文档集成。 项目准…

Label Studio 半自动化标注

引言 Label Studio ML 后端是一个 SDK,用于包装您的机器学习代码并将其转换为 Web 服务器。Web 服务器可以连接到正在运行的 Label Studio 实例,以自动执行标记任务。我们提供了一个示例模型库,您可以在自己的工作流程中使用这些模型,也可以根据需要进行扩展和自定义。 1…

Ubuntu22.04环境下源码安装OpenCV 4.8.1

因为项目需要用OpenCV对yolov8模型进行推理&#xff0c;通过DNN模块&#xff0c;之前本地的OpenCV版本是4.5.4&#xff08;好像安装完ROS2 humble之后系统就自带了opencv&#xff09;&#xff0c;加载onnx模型一直报错&#xff0c;网上查询到需要4.7以上&#xff0c;干脆直接升…

开发教程 | 插件使用常见问题与调用攻略

Q1&#xff1a;插件是什么&#xff1f; 插件可以理解为是在一些专业领域上的单独的专精模型&#xff0c;比如专门生成PPT的模型、专门生成简历的模型。 大模型本身其实只是一个文字生成工具&#xff0c;只能根据自己在预训练过程中投入的语料以及用户的指令来回答问题。这给大…

内核定时器API实现点灯

1.内核定时器 定时器是一个很常用的功能&#xff0c;需要周期性处理的工作都要用到定时器。 Linux 内核定时器 采用系统时钟来实现&#xff0c;并不是6ull里面的硬件定时器。 Linux 内核定时器使用很简单&#xff0c;只需要提供超时时间(相当于定时值)和定时处理函数即…

500万人报名的软考到底是什么?有什么用?考什么?怎么报名?

软考是目前中国计算机领域最权威的认证考试之一&#xff0c;被广大IT从业者视为职业生涯发展的重要里程碑。通过参加软考&#xff0c;考生可以获得国家级资格认证&#xff0c;证明其具备一定的计算机专业知识和技能。本文将详细介绍软考的相关信息&#xff0c;帮助读者了解软考…

docker compose入门2—docker-compose.yaml中的version表示什么意思

在 Docker Compose 中&#xff0c;version 字段用于指定 docker-compose.yml 文件的版本格式。不同版本定义了不同的功能和语法&#xff0c;因此 version 决定了你能够使用哪些特性和指令。 常见的 Compose 文件版本 Version 1: 不需要明确指定 version 字段。只支持最基础的功…

大数据-159 Apache Kylin 构建Cube 准备和测试数据

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 目前已经更新到了&#xff1a; Hadoop&#xff08;已更完&#xff09;HDFS&#xff08;已更完&#xff09;MapReduce&#xff08;已更完&am…

QT TCP服务器/客户端

服务器 首先要在.pro文件中添加network&#xff0c;否则将不能使用QTcpserver QT core gui network#ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QTcpServer> #include <QTcpSocket> #define PORT 8000QT_BEGIN_NAMESPACE namesp…

【网络安全】-web安全-基础知识梳理

1.渗透测试流程&#xff1a; 信息收集 1、获取域名的whois信息,获取注册者邮箱姓名电话等&#xff0c;丢社工库里看看有没有泄露密码&#xff0c;然后尝试用泄露的密码进行登录后台&#xff0c;用邮箱做关键词进行丢进搜索引擎。利用搜索到的关联信息找出其他邮箱进而得到常用…

使用Rollup.js快速开始构建一个前端项目

Rollup 是一个用于 JavaScript 项目的模块打包器&#xff0c;它将小块代码编译成更大、更复杂的代码&#xff0c;例如库或应用程序。Rollup 对代码模块使用 ES6 模块标准&#xff0c;它支持 Tree-shaking&#xff08;摇树优化&#xff09;&#xff0c;可以剔除那些实际上没有被…