源码解读笔记:协程的 ViewModel.viewModelScope和LifecycleOwner.lifecycleScope
分析下ViewModel.viewModelScope
public val ViewModel.viewModelScope: CoroutineScopeget() {val scope: CoroutineScope? = this.getTag(JOB_KEY)if (scope != null) {return scope}return setTagIfAbsent(JOB_KEY,CloseableCoroutineScope(SupervisorJob() + Dispatchers.Main.immediate))}internal class CloseableCoroutineScope(context: CoroutineContext) : Closeable, CoroutineScope {override val coroutineContext: CoroutineContext = contextoverride fun close() {coroutineContext.cancel()}
}
1. ViewModel.viewModelScope:
这是一个公共的只读属性,用于获取或创建与 ViewModel 关联的 CoroutineScope。
get 函数用于计算属性值。它首先尝试从 ViewModel 的 tag(存储在 this.getTag(JOB_KEY) 中)获取已存在的 CoroutineScope。
如果找到了已存在的 CoroutineScope(scope != null),则直接返回它。
如果没有找到,它将创建一个新的 CloseableCoroutineScope 并将其设置为 ViewModel 的 tag,同时返回新创建的 CoroutineScope。
创建的 CoroutineScope 由一个 SupervisorJob 和 Dispatchers.Main.immediate 组成,这表示协程将在主线程上运行,并且具有超时监督策略。
2. CloseableCoroutineScope:
这是一个内部类,实现了 Closeable 和 CoroutineScope 接口。
实现了 CoroutineScope.coroutineContext 属性,它返回给定的 CoroutineContext 参数,通常是一个组合了 Job 和 Dispatcher 的上下文。
实现了 Closeable.close() 方法,当调用 close() 时,它会取消关联的 CoroutineContext,这通常意味着取消所有正在运行的协程。
总结一下,这段代码的主要目的是为 ViewModel 提供一个可管理的协程作用域,这个作用域与 ViewModel 的生命周期绑定,并且可以在需要时安全地取消所有协程。
CloseableCoroutineScope 确保了协程的生命周期管理,当不再需要时可以被关闭,从而避免资源泄漏。Dispatchers.Main.immediate 确保所有在此作用域内启动的协程都在主线程上执行,这对于更新 UI 是必要的。
3. 其中的SupervisorJob()
SupervisorJob 是 Kotlin 协程库中的一个类,它是 Job 的一个子类,主要用于管理协程的生命周期。
SupervisorJob 的主要特点是它采用了所谓的“非传播”异常策略,这意味着如果在其子协程中发生异常,SupervisorJob 不会因为这些异常而自动取消自身或其他子协程。
这种设计是为了防止一个子任务的错误导致整个工作树的崩溃,使得其他任务有机会完成或者独立处理错误。
使用 SupervisorJob 的场景通常包括:
- 错误隔离:如果你希望一个子任务的失败不会影响其他子任务,可以使用 SupervisorJob 创建这些子任务的父级作用域。
- 独立性:在多个任务之间需要保持独立性,即使其中一个失败,其他任务也应该继续运行。
- 资源清理:在某些情况下,你可能希望即使有子任务失败,仍然能够执行清理操作,如关闭文件流或网络连接。
在上述代码中,SupervisorJob() 被用来创建一个 CloseableCoroutineScope 的上下文,这意味着在这个作用域内启动的所有协程都将受到 SupervisorJob 的管理,它们会在主线程上运行,并且即使其中一个协程由于异常而终止,其他协程仍将继续执行。
这对于 ViewModel 来说是一个很好的选择,因为它允许 ViewModel 中的不同任务独立处理错误,而不是整体崩溃。
分析下LifecycleOwner.lifecycleScope:
public val LifecycleOwner.lifecycleScope: LifecycleCoroutineScopeget() = lifecycle.coroutineScope
定义了一个扩展属性 coroutineScope,适用于 Lifecycle 类型的对象。这个属性返回一个 LifecycleCoroutineScope 对象。
public val Lifecycle.coroutineScope: LifecycleCoroutineScopeget() {while (true) {val existing = mInternalScopeRef.get() as LifecycleCoroutineScopeImpl?if (existing != null) {return existing}val newScope = LifecycleCoroutineScopeImpl(this,SupervisorJob() + Dispatchers.Main.immediate)if (mInternalScopeRef.compareAndSet(null, newScope)) {newScope.register()return newScope}}}
- 使用无限循环来确保在多线程环境下正确获取或创建 LifecycleCoroutineScope。
- 检查现有作用域:从 mInternalScopeRef 中获取现有的 LifecycleCoroutineScopeImpl 实例。 如果存在现有实例,则直接返回该实例。
- 否则,创建新作用域:创建一个新的 LifecycleCoroutineScopeImpl 实例。
- this 表示当前的 Lifecycle 对象。
- SupervisorJob() 创建一个监督者作业,允许子作业独立于父作业失败。
- Dispatchers.Main.immediate 确保协程在主线程上立即执行
- 原子性设置:使用 compareAndSet 方法尝试原子地将 mInternalScopeRef 设置为新的 newScope。
- 如果设置成功,则调用 newScope.register() 注册新作用域,并返回新作用域。
- 如果设置失败(即已经有其他线程设置了 mInternalScopeRef),则继续循环,重新检查现有作用域。
总结
- 目的:这个属性 coroutineScope 提供了一个与 Lifecycle 绑定的协程作用域,确保在生命周期管理下安全地执行协程。
- 线程安全:通过无限循环和 compareAndSet 方法,确保在多线程环境下正确创建和获取 LifecycleCoroutineScope。
- 作用域配置:使用 SupervisorJob 和 Dispatchers.Main.immediate 配置协程作用域,确保协程在主线程上立即执行,并且子协程可以独立于父协程失败。
使用场景 - 生命周期绑定:在 Fragment 或 Activity 中使用 lifecycleScope 可以确保协程在组件的生命周期内安全地执行,避免内存泄漏和资源浪费。
- 主线程操作:适用于需要在主线程上执行的 UI 相关操作,如更新界面、处理用户交互等
相关文章:
源码解读笔记:协程的 ViewModel.viewModelScope和LifecycleOwner.lifecycleScope
分析下ViewModel.viewModelScope public val ViewModel.viewModelScope: CoroutineScopeget() {val scope: CoroutineScope? this.getTag(JOB_KEY)if (scope ! null) {return scope}return setTagIfAbsent(JOB_KEY,CloseableCoroutineScope(SupervisorJob() Dispatchers.Ma…...
11.27周三F34-Day8打卡
文章目录 1. 学习让我感觉很棒。(什么关系?动作 or 描述?主语部分是?)解析答案:【解析答案分析】【对比分析】【拓展内容】2. 她忽然想起来钥匙放另一个包里了。解析答案:【拓展内容】3. 她来不来都没关系。(该由什么引导?这句话又属于什么关系,动作 or 描述?)解析答案…...

XG(S)-PON原理
前言 近年来,随着全球范围内接入市场的飞快发展以及全业务运营的快速开展,已有的PON技术标准在带宽需求、业务支撑能力以及接入节点设备和配套设备的性能提升等方面都面临新的升级需求XG(S)-PON(10G GPON)是在已有GPON技术标准上演进的增强下一代GPON技…...

C语言实例之9斐波那契数列实现
1. 斐波那契数列简介 斐波那契数列(Fibonacci sequence),又称黄金分割数列,因数学家莱昂纳多・斐波那契(Leonardo Fibonacci)以兔子繁殖为例子而引入,故又称为 “兔子数列”。 它的特点是从第三…...

YOLO系列论文综述(从YOLOv1到YOLOv11)【第1篇:概述物体检测算法发展史、YOLO应用领域、评价指标和NMS】
目录 1 前言2 YOLO在不同领域的应用3 物体检测指标和NMS3.1 mAP和IOU3.2 mAP计算流程3.2.1 VOC 数据集3.2.2 微软 COCO 数据集 3.3 NMS 1 前言 最近在做目标检测模型相关的优化,重新看了一些新的论文,发现了几篇写得比较好的YOLO系列论文综述࿰…...

数据结构--Map和Set
目录 一.二叉搜索树1.1 概念1.2 二叉搜索树的简单实现 二.Map2.1 概念2.2 Map常用方法2.3 Map使用注意点2.4 TreeMap和HashMap的区别2.5 HashMap底层知识点 三.Set3.1 概念3.2 Set常用方法3.3 Set使用注意点3.4 TreeSet与HashSet的区别 四.哈希表4.1 概念4.2 哈希冲突与避免4.3…...

计算机操作系统——进程控制(Linux)
进程控制 进程创建fork()函数fork() 的基本功能fork() 的基本语法fork() 的工作原理fork() 的典型使用示例fork() 的常见问题fork() 和 exec() 结合使用总结 进程终止与$进程终止的本质进程终止的情况正常退出(Exit)由于信号终止非…...

【前端】ES6基础
1.开发工具 vscode地址 :https://code.visualstudio.com/download, 下载对应系统的版本windows一般都是64位的 安装可以自选目录,也可以使用默认目录 插件: 输入 Chinese,中文插件 安装: open in browser,直接右键文件…...
【排序算法 python实现】
排序算法 python实现 / 默写 # 汉诺塔 import copy import randomdef hanuo(n, a, b, c):if n 1:print(f{a} --> {c})returnhanuo(n - 1, a, c, b)print(f{a} --> {c})hanuo(n - 1, b, a, c)hanuo(3, A, B, C)# 冒泡排序 def bubble_sort(arr):n len(arr)for i in ran…...

Java图书管理系统(简易保姆级)
前面学习了这么多知识,为了巩固之前的知识,我们就要写一个图书管理系统来帮助大家复习,让大家的知识融会贯通~~~ 话不多说,直接开始今天的内容~ 首先呢,我们要有一个大体的思路: 实现效果思路有两种情况&a…...
嵌入式硬件设计:从概念到实现的全流程
嵌入式硬件设计是现代电子技术中一个至关重要的领域,涉及从硬件架构设计到硬件调试的各个方面。它为我们日常生活中的各类智能设备、家电、工业控制系统等提供了强大的支持。本文将介绍嵌入式硬件设计的基本流程、关键技术、常用工具以及常见的挑战和解决方案&#…...
第 4 章 Java 并发包中原子操作类原理剖析
原子变量操作类 AtomicLong 是原子性递增或者递减类,其内部使用 Unsafe 来实现,AtomicLong类也是在 rt.jar 包下面的,AtomicLong 类就是通过 BootStarp 类加载器进行加载的。这里的原子操作类都使用 CAS 非阻塞算法 private static final lon…...

从 0 到 1 掌握部署第一个 Web 应用到 Kubernetes 中
文章目录 前言构建一个 hello world web 应用项目结构项目核心文件启动项目 检查项目是否构建成功 容器化我们的应用编写 Dockerfile构建 docker 镜像推送 docker 镜像仓库 使用 labs.play-with-k8s.com 构建 Kubernetes 集群并部署应用构建 Kubernetes 集群环境编写部署文件 总…...

政安晨【零基础玩转各类开源AI项目】探索Cursor-AI Coder的应用实例
目录 Cusor的主要特点 Cusor实操 政安晨的个人主页:政安晨 欢迎 👍点赞✍评论⭐收藏 希望政安晨的博客能够对您有所裨益,如有不足之处,欢迎在评论区提出指正! Cursor 是 Visual Studio Code 的一个分支。这使我们能够…...

CentOS 7 安装部署 KVM
1.关闭虚拟机 打开相关选项 打开虚拟机centos7 连接xshell 测试网络,现在就是没问题的,因为我们要使用网络源 安装 GNOME 桌面环境 安装KVM 模块 安装KVM 调试工具 构建虚拟机的命令行工具 qemu 组件,创建磁盘、启动虚拟机等 输入这条命令,…...

ArcGIS 10.2软件安装包下载及安装教程!
今日资源:ArcGIS 适用系统:WINDOWS 软件介绍:ArcGIS是一款专业的电子地图信息编辑和开发软件,提供一种快速并且使用简单的方式浏览地理信息,无论是2D还是3D的信息。软件内置多种编辑工具,可以轻松的完成地…...

一个专为云原生环境设计的高性能分布式文件系统
大家好,今天给大家分享一款开源创新的分布式 POSIX 文件系统JuiceFS,旨在解决海量云存储与各类应用平台(如大数据、机器学习、人工智能等)之间高效对接的问题。 项目介绍 JuiceFS 是一款面向云原生设计的高性能分布式文件系统&am…...

基于深度学习CNN算法的花卉分类识别系统01--带数据集-pyqt5UI界面-全套源码
文章目录 基于深度学习算法的花卉分类识别系统一、项目摘要二、项目运行效果三、项目文件介绍四、项目环境配置1、项目环境库2、环境配置视频教程 五、项目系统架构六、项目构建流程1、数据集2、算法网络Mobilenet3、网络模型训练4、训练好的模型预测5、UI界面设计-pyqt56、项目…...
3174、清除数字
3174、[简单] 清除数字 1、题目描述 给你一个字符串 s 。你的任务是重复以下操作删除 所有 数字字符: 删除 第一个数字字符 以及它左边 最近 的 非数字 字符。 请你返回删除所有数字字符以后剩下的字符串。 2、解题思路 遍历字符串: 我们需要逐个遍…...

C++ 优先算法 —— 无重复字符的最长子串(滑动窗口)
目录 题目: 无重复字符的最长子串 1. 题目解析 2. 算法原理 Ⅰ. 暴力枚举 Ⅱ. 滑动窗口(同向双指针) 3. 代码实现 Ⅰ. 暴力枚举 Ⅱ. 滑动窗口 题目: 无重复字符的最长子串 1. 题目解析 题目截图: 此题所说的…...
uniapp 对接腾讯云IM群组成员管理(增删改查)
UniApp 实战:腾讯云IM群组成员管理(增删改查) 一、前言 在社交类App开发中,群组成员管理是核心功能之一。本文将基于UniApp框架,结合腾讯云IM SDK,详细讲解如何实现群组成员的增删改查全流程。 权限校验…...
web vue 项目 Docker化部署
Web 项目 Docker 化部署详细教程 目录 Web 项目 Docker 化部署概述Dockerfile 详解 构建阶段生产阶段 构建和运行 Docker 镜像 1. Web 项目 Docker 化部署概述 Docker 化部署的主要步骤分为以下几个阶段: 构建阶段(Build Stage):…...

树莓派超全系列教程文档--(61)树莓派摄像头高级使用方法
树莓派摄像头高级使用方法 配置通过调谐文件来调整相机行为 使用多个摄像头安装 libcam 和 rpicam-apps依赖关系开发包 文章来源: http://raspberry.dns8844.cn/documentation 原文网址 配置 大多数用例自动工作,无需更改相机配置。但是,一…...
脑机新手指南(八):OpenBCI_GUI:从环境搭建到数据可视化(下)
一、数据处理与分析实战 (一)实时滤波与参数调整 基础滤波操作 60Hz 工频滤波:勾选界面右侧 “60Hz” 复选框,可有效抑制电网干扰(适用于北美地区,欧洲用户可调整为 50Hz)。 平滑处理&…...
java调用dll出现unsatisfiedLinkError以及JNA和JNI的区别
UnsatisfiedLinkError 在对接硬件设备中,我们会遇到使用 java 调用 dll文件 的情况,此时大概率出现UnsatisfiedLinkError链接错误,原因可能有如下几种 类名错误包名错误方法名参数错误使用 JNI 协议调用,结果 dll 未实现 JNI 协…...

LeetCode - 394. 字符串解码
题目 394. 字符串解码 - 力扣(LeetCode) 思路 使用两个栈:一个存储重复次数,一个存储字符串 遍历输入字符串: 数字处理:遇到数字时,累积计算重复次数左括号处理:保存当前状态&a…...

新能源汽车智慧充电桩管理方案:新能源充电桩散热问题及消防安全监管方案
随着新能源汽车的快速普及,充电桩作为核心配套设施,其安全性与可靠性备受关注。然而,在高温、高负荷运行环境下,充电桩的散热问题与消防安全隐患日益凸显,成为制约行业发展的关键瓶颈。 如何通过智慧化管理手段优化散…...
GitHub 趋势日报 (2025年06月08日)
📊 由 TrendForge 系统生成 | 🌐 https://trendforge.devlive.org/ 🌐 本日报中的项目描述已自动翻译为中文 📈 今日获星趋势图 今日获星趋势图 884 cognee 566 dify 414 HumanSystemOptimization 414 omni-tools 321 note-gen …...

JUC笔记(上)-复习 涉及死锁 volatile synchronized CAS 原子操作
一、上下文切换 即使单核CPU也可以进行多线程执行代码,CPU会给每个线程分配CPU时间片来实现这个机制。时间片非常短,所以CPU会不断地切换线程执行,从而让我们感觉多个线程是同时执行的。时间片一般是十几毫秒(ms)。通过时间片分配算法执行。…...

QT: `long long` 类型转换为 `QString` 2025.6.5
在 Qt 中,将 long long 类型转换为 QString 可以通过以下两种常用方法实现: 方法 1:使用 QString::number() 直接调用 QString 的静态方法 number(),将数值转换为字符串: long long value 1234567890123456789LL; …...