android kotlin 协程(五) suspend与continuation

news2024/11/24 1:47:50

android kotlin 协程(五) suspend与continuation

通过本篇你将学会:

  • suspendCoroutine{}

  • suspendCancellableCoroutine{}

  • suspend 与 continuation

suspendCoroutine

第一次看到这玩意的时候肯定有点身体不适, 先不用管这个东西是什么,

目前为止 只需要知道 suspendCoroutine是一个函数即可

先来想想如果不用这个suspendCoroutine ,遇到一个网络请求的原始写法是怎么样的

通常情况下,我们请求一个接口,至少需要处理2种情况

  • 成功返回
  • 失败返回

来看例子:

private suspend fun requestLoginNetworkData(account: String, pwd: String) =
    withContext(Dispatchers.IO) {
        delay(2000)// 模拟请求耗时
        if (account == "123456789" && pwd == "666666") {
            Result.success("登陆成功")
        } else {
            Result.failure(Throwable("登陆失败"))
        }
    }

fun main() = runBlocking<Unit> {
    val deferred = async {
        // 模拟网络请求
        requestLoginNetworkData("987654321", "666666")
    }
    // 获取网络返回数据,判断成功与失败
    val result = deferred.await()

    // result.getOrDefault("") // 如果返回错误使用 默认值
    // result.getOrThrow() // 如果返回错误使用 错误
    // result.getOrNull() // 如果返回错误使用 null
    result.onSuccess {
        println("登陆成功:${result.getOrNull()}")
    }.onFailure {
        println("登陆失败:${result.getOrNull()}")
    }
}

在这段代码中,我们模拟网络请求, 给一个错误的帐号密码,最终打印结果为

登陆失败:null

通过前几篇的了解,这个例子应该是非常简单的

来看看使用 suspendCoroutine怎么玩

private suspend fun <T> requestLoginNetworkData(account: String, pwd: String): String {
    return withContext(Dispatchers.IO) {
        delay(2000)  // 模拟网络耗时需要2s
        return@withContext suspendCoroutine {
            if (account == "123456789" && pwd == "666666") {
                it.resume("登陆成功")
            } else {
                it.resumeWithException(RuntimeException("登陆失败"))
            }
        }
    }
}

suspend fun main() = runBlocking {
    val scope = CoroutineScope(Dispatchers.IO)
    // 开启一个协程
    val deferred = scope.async {
        // 模拟网络请求
        requestLoginNetworkData<String>("987654321", "666666")
    }
    // 获取网络返回数据,判断成功与失败
    val result = runCatching {
        deferred.await()
    }

    if (result.isSuccess) {
        printlnThread("登陆成功:${result.getOrNull()}")
    } else {
        printlnThread("登陆失败:${result.exceptionOrNull()}")
    }
}

好像使用suspendCoroutine 之后代码变得更多了?

来看看两段代码的区别:

image-20230221144750274

这两段代码,只不过是回调方式不同!

那么是否可以理解为 suspendCoroutine 本质就是一个回调呢?

没错! 暂时可以理解为:suspendCoroutine 就是一个回调

再来看看 suspendCoroutine的具体实现

image-20230221145402455

其本质就是一个Continuation

tips: Continuation 这个角色特别重要,Continuation 是用来使挂起函数恢复执行状态的

就是传说中: kotlin挂起于恢复中的 恢复

再来看看调用的方法:

image-20230221145610113

  • resume 恢复正确
  • resumeWithException 恢复错误

目前不理解恢复没关系, 先理解为就是一个接口回调

  • resume 回调正确
  • resumeWithException 回调错误

suspendCoroutine 的本质作用是创建一个挂起点,它会将当前协程挂起,并将协程的执行权交给调用方函数。同时,它会传入一个 Continuation 对象,该对象包含了协程的上下文和协程恢复后需要执行的操作。调用方函数可以在执行完必要的操作后,调用该 Continuation 对象的 resume 方法,来唤醒协程并继续执行。

suspendCoroutine 是一个非常重要的函数,它可以让我们将异步操作转化为同步代码风格

说的直白一点就是:

  • 不使用 suspendCoroutine 执行一个suspend的函数的时候, 恢复工作由系统完成

  • 使用 suspendCoroutine会将系统的恢复工作抢过来,可以通过 continuation#resume() 来自己恢复

例如这样,我们手动处理了 suspendCoroutine,但是没有恢复, 就会无限挂起

image-20230221151954694

我们知道在kotlin中有suspend,但是在java中并没有suspend关键字,

那么kotlin suspend函数反编译成java后是什么样的

image-20230221152627482

可以看出,suspend关键字并没有任何作用, 他的唯一作用就是告诉开发者,我这里需要挂起罢了

真实干活的其实是 Continuation!

现在你还觉得 suspendCoroutine 仅仅只是一个回调嘛?

suspendCoroutine 不仅可以控制suspend函数的恢复,而且还可以让异步的代码同步化.

最关键的是线程, 线程安全不用我们担心.

来比较一下同步代码与异步代码的风格写法:

image-20230221160111473

也没说异步写法不好,黑猫白猫,抓住老鼠就是好猫,但是这只是一个请求,如果说 逻辑很多,嵌套很深的话,代码会不会成这样:

IMG_7411

suspendCancellableCoroutine

suspendCoroutine 与 suspendCancellableCoroutine 的区别:

suspendCancellableCoroutine 相当于是对 suspendCoroutine 的一次封装, 增加了一些 状态,以及 可以 cancel了

  • isActive 是否活跃
  • isCancelled 是否取消
  • isCompleted 是否执行完成

还记得这三个状态吗? Job中也有这三个状态!

suspendCancellableCoroutine 增加了 invokeOnCancellation , 该方法用来监听协程取消, 当协程被取消的时候会被回调

来看看下面的例子:

image-20230222174735212

可以看到,invokeOnCancellation 并没有执行,这里也很好理解,因为没有cancel不执行也正常

在换一个例子

image-20230222141656317

这里的关键点是await, 这是官方的一个扩展,来看看:

image-20230222141919812

这段代码对Cell扩展了一下, 请求数据的时,

  • 请求成功 就恢复
  • 请求失败 也恢复,只不过会throw异常

当协程取消的时候,将okhttp cancel掉

invokeOnCancellation 注意事项

在使用suspendCancellableCoroutine的时候,有一个方法 invokeOnCancellation

这个方法用来监听当前作用域是否取消

先来看看运行的3种状态:

  • isActive 是否活跃
  • isCancelled 是否取消
  • isCompleted 是否执行完成

image-20230222190707683

当我们调用 Continuation#resume()恢复之后, 当前协程就会被标记为完成状态

这里有一个小细节:Continuation#cancel() 只能cancel未完成或在进行中的协程, 如果协程一旦执行完成,也就是一旦恢复,那么 invokeOnCancellation则不会被调用

再来看看取消:

image-20230222193217557

这种情况,应该大家看看就会了很好理解

还有一种写法, 我们知道,当我们cancel父协程的时候,所有子协程也会被cancel,那么我们就可以利用这个特性,来完成这个效果

例如这样:

image-20230222201240817

这里有一个很关键的点,折磨了我很久:)

当一个挂起函数中的suspendCancellableCoroutine函数被恢复(例如,通过调用continuation.resumecontinuation.resumeWithException)后,该协程就不再挂起,并且不能再被取消。因此,在恢复之后,该协程将无法响应invokeOnCancellation函数。

完整代码

下篇开始会看看协程源码, 以及手动创建协程等

下篇预告:

  • SafeContinuation
  • startCoroutine
  • createCoroutine
  • receiver startCoroutine
  • receiver createCoroutine

原创不易,您的点赞就是对我最大的支持!

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

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

相关文章

谈一谈你对View的认识和View的工作流程

都2023年了&#xff0c;不会还有人不知道什么是View吧&#xff0c;不会吧&#xff0c;不会吧。按我以往的面试经验来看&#xff0c;View被问到的概率不比Activity低多少哦&#xff0c;个人感觉View在Android中的重要性也和Activity不相上下&#xff0c;所以这篇文章将介绍下Vie…

【Python工具】WiFi工具

文章目录前言一、介绍二、使用步骤1.完整代码2.简单使用总结免责声明&#xff1a;前言 注意&#xff1a;此工具为自己测试&#xff0c;不可用作恶意使用。 考虑在windows中的wifi破解工具五花八门很多的广告程序&#xff0c;就自己写一个&#xff0c;原理比较简单&#xff0c…

11技术太卷我学APEX-数据加载

11技术太卷我学APEX-数据加载 0 所谓的数据加载 就是导入数据到数据库表中&#xff0c;本示例就采用Excel导入数据到《技术太卷我学APEX》的apex_learn表。表结构大概是这样的 CREATE TABLE "APEX_LEARN" ( "P_ID" NUMBER(17,0) NOT NULL ENABLE, &quo…

PMP真的有那么厉害?你需要考PMP吗?

这个含金量是有的&#xff0c;是目前项目管理界含金量较高的证书&#xff0c;但也要分人&#xff0c; 因为这是职业证书&#xff0c;主要用于提高职场工作能力&#xff0c;不搞这一行的&#xff0c;PMP证书含金量再高也是一张废纸&#xff0c;可以看下下面这张图&#xff0c;这…

【Redis】Redis 的过期策略以及内存淘汰机制详解

Redis 的过期策略以及内存淘汰机制详解1. Redis 的过期策略1.1 如何设置 key 的过期时间&#xff1f;1.2 key 设置且到了过期时间后&#xff0c;该 key 保存的数据还占据内存么&#xff1f;1.3 Redis 如何删除过期的数据1.3.1 定期删除1.3.2 惰性删除2. Redis 的内存淘汰机制2.…

智能新冠疫苗接种助手管理系统

项目背景介绍 近几年来,网络事业&#xff0c;特别是Internet发展速度之快是任何人都始料不及的。目前&#xff0c;由于Internet表现出来的便捷&#xff0c;快速等诸多优势&#xff0c;已经使它成为社会各行各业&#xff0c;甚至是平民大众工作&#xff0c;生活不可缺少的一个重…

【UE4 制作自己的载具】2-导入UE

在上一篇博客中&#xff08;【UE4 制作自己的载具】1-使用3dsmax制作载具&#xff09;已经完成了载具模型的制作&#xff0c;本篇需要完成的是将模型导入UE中并进行一些调整。本篇制作步骤&#xff1a;新建一个UE工程&#xff0c;可以添加一个载具模板创建一个“Vehicle”文件夹…

windows服务器上传文件解决方案

1.说明 1.如果上传到linux系统&#xff0c;通常使用ftp相关技术&#xff0c;配合windows端的ftp客户端工具比如FileZilla等进行大文件的上传工作。 2.同理windows服务器也可以开启ftp服务用来传输大文件。 3.本文介绍偷懒方式&#xff08;常规是开启windows的ftp服务&#xff0…

案例|山东省中医院基于ZABBIX构建网络设备监控预警平台

作者介绍&#xff1a;董晨&#xff0c;山东省中医院信息科机房运维管理 本文从背景、演进、成效来分享建设过程&#xff0c;最终得出结论&#xff0c;类似Zabbix的国产成熟产品市场价值动辄上百万&#xff0c;而我们以极小成本&#xff0c;为医院节省了大量资金&#xff0c;取…

Java多线程(一)--多线程基础知识

1. 为什么要使用并发编程提升多核CPU的利用率&#xff1a;一般来说一台主机上的会有多个CPU核心&#xff0c;我们可以创建多个线程&#xff0c;理论上讲操作系统可以将多个线程分配给不同的CPU去执行&#xff0c;每个CPU执行一个线程&#xff0c;这样就提高了CPU的使用效率&…

通过NPM生态系统中的依赖树揭开脆弱性传播及其演化的神秘面纱

通过NPM生态系统中的依赖树揭开脆弱性传播及其演化的神秘面纱 本文实现了一个依赖约束解析器来解决NPM依赖约束的多样性&#xff0c;并在此基础上构建了一个完整的依赖漏洞知识图&#xff08;DVGraph&#xff09;&#xff0c;以捕获所有NPM包之间的依赖关系。 https://www.se…

关于thumb2指令下函数运行地址对齐问题及验证固件分析

近期&#xff0c;在分析Thumb2指令的一个固件文件时&#xff0c;发现Thumb2指令集下执行函数的运行地址不对齐&#xff1f; 于是&#xff0c;为了分析一下原因&#xff0c;找了手头上现有的一款基于Cortex3内核的一块板子来实际执行看一下&#xff0c;结合编译后的Hex文件&…

【性能测试】loadrunner(一)知识准备

【性能测试】loadrunner&#xff08;一&#xff09;知识准备 目录&#xff1a;导读 1.0. 前言 1.1 性能测试术语介绍 1.2 性能测试分类 1.3 HTTP我们需要知道的 1.4 Loadrunner 12.55安装 1.0. 前言 ​ 在性能测试中&#xff0c;牵扯到了许多比较杂的知识点&#xff0c;…

Java文件IO操作:File类的相关内容

Java文件IO操作一、File类1.相对路径和绝对路径2.路径分隔符&#xff08;同一路径下、多个路径下&#xff09;3.实例化4.常见方法一、File类 File类继承自Object类&#xff0c;实现了Serializable接口和Comparable接口&#xff1b; File类属于java.io包&#xff1b; File类是文…

高通平台开发系列讲解(WIFI篇)802.11 基本概念

文章目录 一、WLAN概述二、802.11发展历程三、802.11基本概念沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本文将基于高通平台介绍802.11基本概念。 一、WLAN概述 WLAN是Wireless Local Area Network的简称,指应用无线通信技术将计算机设备互联起来,构成可以互相通…

Python计算 -- 内附蓝桥题:相乘,三角回文数

计算 ~~不定时更新&#x1f383;&#xff0c;上次更新&#xff1a;2023/02/23 &#x1f5e1;常用函数&#xff08;方法&#xff09; 1. 求一个整数的最末位 举个栗子&#x1f330; n int(input()) end n % 10蓝桥例题1 - 相乘✖️ 题目描述 本题为填空题&#xff0c;…

【python学习笔记】:输出与输入

01 输出方式 表达式语句、print()函数和使用文件对象的write()方法。 02 输出形式 格式化输出str.format()函数、转成字符串可以使用repr()或str()函数来实现。 (1)repr()&#xff1a;产生一个解释器易读的表达形式&#xff0c;便于字符串的拼接。 例&#xff1a;输出平方与…

OpenGL超级宝典学习笔记:着色器存储区块、原子内存操作、内存屏障

前言 本篇在讲什么 本篇为蓝宝书学习笔记 着色器存储区块 原子内存操作 内存屏障 本篇适合什么 适合初学Open的小白 本篇需要什么 对C语法有简单认知 对OpenGL有简单认知 最好是有OpenGL超级宝典蓝宝书 依赖Visual Studio编辑器 本篇的特色 具有全流程的图文教学 重…

五月天演唱会门票开售,retry了一小时也没re进去!到底是什么高科技在作祟?!

等待了三年&#xff0c;大型线下演唱会终于回归了&#xff01;前段时间&#xff0c;文化和旅游部市场管理司发布通知称&#xff0c;自2023年2月16日起&#xff0c;各地文化和旅游行政部门恢复对涉港澳台营业性演出的受理和审批。据中国演出行业协会在业内调研了解&#xff0c;周…

Python Pytorch开发环境搭建(Windows和Ubuntu)

Python Pytorch开发环境搭建&#xff08;Windows和Ubuntu&#xff09; 目录 Pytorch开发环境搭建 1. 安装cuda cudnn (1)Windows安装方法 (2)Ubuntu18.04安装方法 2. 安装Python(推荐使用Anaconda) (1)Windows安装方法 (2)Ubuntu18.04安装方法 3. Pytorch安装 4. 安装…