Kotlin 协程:用源码来理解 ‘viewModelScope‘

Kotlin 协程:用源码来理解 ‘viewModelScope’

在这里插入图片描述

Kotlin 协程是 Kotlin 语言的一大特色,它让异步编程变得更简单。在 Android 开发中,我们经常需要在后台线程执行耗时操作,例如网络请求或数据库查询,然后在主线程更新 UI。Kotlin 协程让我们可以用同步的方式写异步代码,使得代码更易读、更易写。

在这篇文章中,我们将通过分析源码来深入理解 Kotlin 协程中的 viewModelScopeviewModelScope 是 Android 架构组件库中 ViewModel 类的一个扩展属性,它为 ViewModel 提供了一个协程作用域。在这个作用域中启动的所有协程都会在 ViewModel 清除时自动取消,防止内存泄漏。

Kotlin 协程简介

在我们深入 viewModelScope 的源码之前,让我们先简单回顾一下 Kotlin 协程的基础知识。

Kotlin 协程是一种在 Kotlin 语言中实现轻量级线程的机制。它可以让我们在不阻塞线程的情况下挂起和恢复函数的执行。这意味着我们可以在主线程中执行耗时操作,而不会阻塞 UI。

Kotlin 协程的核心是 suspend 关键字。它可以将一个函数标记为挂起函数。挂起函数可以在不阻塞线程的情况下挂起和恢复执行。挂起函数只能在协程或其他挂起函数中调用。

suspend fun fetchDataFromNetwork() {// 在这里执行网络请求
}

在上面的例子中,fetchDataFromNetwork 是一个挂起函数。当我们在协程中调用这个函数时,它会挂起协程的执行,执行网络请求,然后恢复协程的执行。在这个过程中,线程不会被阻塞,所以我们可以在主线程中安全地调用这个函数。

viewModelScope 简介

viewModelScope 是 Android 架构组件库中 ViewModel 类的一个扩展属性。它为 ViewModel 提供了一个协程作用域。在这个作用域中启动的所有协程都会在 ViewModel 清除时自动取消,防止内存泄漏。

class MyViewModel : ViewModel() {init {viewModelScope.launch {// 在这里启动一个新的协程}}
}

在上面的例子中,我们在 MyViewModelviewModelScope 中启动了一个新的协程。由于我们是在 viewModelScope 中启动的这个协程,所以当 MyViewModel 被清除时,这个协程会被自动取消。

这个特性非常有用,因为它可以自动管理协程的生命周期,防止内存泄漏。这使得我们可以在 ViewModel 中安全地启动协程,而不用担心协程的生命周期管理。

在接下来的部分中,我们将深入 viewModelScope 的源码,看看它是如何实现这个特性的。

viewModelScope 的源码分析

viewModelScope 是通过 CoroutineScope 接口实现的。CoroutineScope 是 Kotlin 协程库中的一个接口,它定义了一个协程作用域。在一个 CoroutineScope 中启动的所有协程都属于这个作用域,当这个作用域被取消时,作用域中的所有协程都会被取消。

viewModelScope 的源码如下:

val ViewModel.viewModelScope: CoroutineScopeget() {val scope: CoroutineScope? = this.getTag(JOB_KEY)if (scope != null) {return scope}return setTagIfAbsent(JOB_KEY, ViewModelCoroutineScope(this))}

在这段源码中,viewModelScope 是通过 getTagsetTagIfAbsent 方法来实现的。getTag 方法用于获取 ViewModel 的 viewModelScope,如果 ViewModel 还没有 viewModelScope,那么 setTagIfAbsent 方法会创建一个新的 ViewModelCoroutineScope 并将其设置为 ViewModel 的 viewModelScope

ViewModelCoroutineScope 是一个实现了 CoroutineScope 接口的类。它的源码如下:

class ViewModelCoroutineScope(private val viewModel: ViewModel
) : MainCoroutineScope() {private val job = SupervisorJob().apply {invokeOnCompletion { error ->if (error is CancellationException) {viewModel.clear()}}}override val coroutineContext: CoroutineContextget() = job + Dispatchers.Main
}

在这段源码中,ViewModelCoroutineScope 创建了一个 SupervisorJob,并将其设置为作用域的 jobSupervisorJobJob 的一个子类,它允许其子协程独立地失败,而不会影响其他子协程。

ViewModelCoroutineScope 还重写了 CoroutineScopecoroutineContext 属性,将 jobDispatchers.Main 添加到作用域的上下文中。这意味着在这个作用域中启动的所有协程都会在主线程中运行,并共享同一个 job

job 完成时,invokeOnCompletion 方法会被调用。如果 job 是因为被取消而完成的,那么 viewModel.clear() 方法会被调用,清除 ViewModel 的所有数据。

这就是 viewModelScope 的源码实现。通过这段源码,我们可以看到 viewModelScope 是如何自动管理协程的生命周期的。当 ViewModel 被清除时,viewModelScope 中的所有协程都会被自动取消,防止内存泄漏。

在下一部分中,我们将进一步探讨 viewModelScope 的使用方法和最佳实践。

viewModelScope 的使用方法和最佳实践

在 ViewModel 中使用 viewModelScope 是非常简单的。我们只需要在 viewModelScope 中启动我们的协程,然后 viewModelScope 会自动管理协程的生命周期。

class MyViewModel : ViewModel() {fun fetchData() {viewModelScope.launch {// 在这里启动一个新的协程}}
}

在上面的例子中,我们在 fetchData 方法中启动了一个新的协程。这个协程会在 MyViewModel 被清除时自动取消,防止内存泄漏。

使用 viewModelScope 的一个最佳实践是在 ViewModel 的方法中启动协程,而不是在 ViewModel 的构造函数或 init 块中启动协程。这是因为 ViewModel 的构造函数和 init 块会在 ViewModel 创建时立即执行,而这个时候 ViewModel 可能还没有完全初始化,可能会导致问题。例如,如果我们在 ViewModel 的 init 块中启动一个协程来更新 LiveData,那么这个 LiveData 可能还没有观察者,更新操作可能会被忽略。

因此,我们建议在 ViewModel 的方法中启动协程,这样我们可以在需要时启动协程,而不是在 ViewModel 创建时就启动协程。

在接下来的部分中,我们将通过一个例子来展示如何在实际的 Android 开发中使用 viewModelScope

viewModelScope 的实际应用

让我们来看一个例子,展示如何在实际的 Android 开发中使用 viewModelScope

假设我们正在开发一个天气应用,这个应用有一个 WeatherViewModel,它负责从网络获取天气数据,并更新 UI。

class WeatherViewModel(private val weatherRepository: WeatherRepository) : ViewModel() {val weatherLiveData = MutableLiveData<Weather>()fun fetchWeather(city: String) {viewModelScope.launch {val weather = weatherRepository.fetchWeather(city)weatherLiveData.value = weather}}
}

在这个例子中,我们在 fetchWeather 方法中启动了一个新的协程。这个协程会在 WeatherViewModel 被清除时自动取消,防止内存泄漏。

这就是 viewModelScope 的实际应用。通过使用 viewModelScope,我们可以在 ViewModel 中安全地启动协程,而不用担心协程的生命周期管理。

在接下来的部分中,我们将总结 viewModelScope 的主要特点和优点。

viewModelScope 的总结

viewModelScope 是 Android 架构组件库中 ViewModel 类的一个扩展属性。它为 ViewModel 提供了一个协程作用域。在这个作用域中启动的所有协程都会在 ViewModel 清除时自动取消,防止内存泄漏。

viewModelScope 的主要特点和优点包括:

  • 自动管理协程的生命周期:在 viewModelScope 中启动的所有协程都会在 ViewModel 清除时自动取消,防止内存泄漏。
  • 简化异步编程:Kotlin 协程让我们可以用同步的方式写异步代码,使得代码更易读、更易写。
  • 安全地在主线程中执行耗时操作:Kotlin 协程可以让我们在不阻塞线程的情况下挂起和恢复函数的执行,这意味着我们可以在主线程中执行耗时操作,而不会阻塞 UI。

使用 viewModelScope 的一个最佳实践是在 ViewModel 的方法中启动协程,而不是在 ViewModel 的构造函数或 init 块中启动协程。这是因为 ViewModel 的构造函数和 init 块会在 ViewModel 创建时立即执行,而这个时候 ViewModel 可能还没有完全初始化,可能会导致问题。

结论

通过分析 viewModelScope 的源码,我们可以看到 viewModelScope 是如何自动管理协程的生命周期的。当 ViewModel 被清除时,viewModelScope 中的所有协程都会被自动取消,防止内存泄漏。

Kotlin 协程和 viewModelScope 是 Kotlin 语言和 Android 架构组件库的强大特性,它们可以大大简化我们的异步编程工作,使我们的代码更易读、更易写。

我们希望这篇文章能帮助你更深入地理解 Kotlin 协程和 viewModelScope,并在你的 Android 开发工作中得到应用。

参考

  • Kotlin 协程文档
  • Android 架构组件库文档
  • Kotlin 协程在 Android 中的使用
  • ViewModel 文档

祝你编程愉快!

感谢阅读, Best Regards!

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

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

相关文章

坚持刷题 | 完全二叉树的节点个数

Hello&#xff0c;大家好&#xff0c;我是阿月&#xff01;坚持刷题&#xff0c;老年痴呆追不上我&#xff0c;今天刷&#xff1a;完全二叉树的节点个数 题目 222.完全二叉树的节点个数 代码实现 class TreeNode {int val;TreeNode left, right;public TreeNode(int val) …

Android进阶之路 - ViewPager2 比 ViewPager 强在哪?

我记得前年&#xff08;2022&#xff09;面试的时候有被问到 ViewPager 和 ViewPager2 有什么区别&#xff1f;当时因为之前工作一直在开发售货机相关的项目&#xff0c;使用的技术要求并不高&#xff0c;所以一直没去了解过 ViewPager2~ 去年的时候正好有相关的功能需求&#…

数学建模 - 线性规划入门:Gurobi + python

在工程管理、经济管理、科学研究、军事作战训练及日常生产生活等众多领域中&#xff0c;人们常常会遇到各种优化问题。例如&#xff0c;在生产经营中&#xff0c;我们总是希望制定最优的生产计划&#xff0c;充分利用已有的人力、物力资源&#xff0c;获得最大的经济效益&#…

pytorch_car_caring 排坑记录

pytorch_car_caring 排坑记录 任务踩坑回顾简单环境问题代码版本问题症状描述解决方法 cuda问题&#xff08;异步问题&#xff09;症状描述解决方法 任务 因为之前那个MPC代码跑出来的效果不理想&#xff0c;看了一天代码&#xff0c;大概看明白了&#xff0c;但要做改进还要有…

R-YOLO

Abstract 提出了一个框架&#xff0c;名为R-YOLO&#xff0c;不需要在恶劣天气下进行注释。考虑到正常天气图像和不利天气图像之间的分布差距&#xff0c;我们的框架由图像翻译网络&#xff08;QTNet&#xff09;和特征校准网络&#xff08;FCNet&#xff09;组成&#xff0c;…

【数睿】数睿常见问题处理

连接器请求地址修改 cat /home/sdata2/tomcat/bin/setenv.sh修改里面的 SYSTEM_URL 为数睿服务实际访问地址 如图所示 连接器执行 异常日志 2024-01-23 18:01:49,586 (conf-file-poller-0) [ERROR - org.apache.flume.node.PollingPropertiesFileConfigurationProvider$Fil…

全链路压测的关键点是什么?

全链路压测是一种重要的性能测试方法&#xff0c;用于评估应用程序或系统在真实生产环境下的性能表现。通过模拟真实用户行为和流量&#xff0c;全链路压测能够全面评估系统在不同负载下的稳定性和性能表现。本文将介绍全链路压测的关键点&#xff0c;以帮助企业更好地理解和应…

Redis核心技术与实战【学习笔记】 - 10.浅谈CPU架构对Redis性能的影响

概述 可能很多人都认为 Redis 和 CPU 的关系简单&#xff0c;Redis 的线程在 CPU 上运行&#xff0c;CPU 快 Reids 处理请求的速度也很快。 其实&#xff0c;这种认知是片面的&#xff0c;CPU 的多核架构及多 CPU 结构&#xff0c;也会影响到 Redis 的性能。如果不了解 CPU 对…

【目标检测】对DETR的简单理解

【目标检测】对DETR的简单理解 文章目录 【目标检测】对DETR的简单理解1. Abs2. Intro3. Method3.1 模型结构3.2 Loss 4. Exp5. Discussion5.1 二分匹配5.2 注意力机制5.3 方法存在的问题 6. Conclusion参考 1. Abs 两句话概括&#xff1a; 第一个真正意义上的端到端检测器最…

实习日志10

1.用户信息 1.1.在用户管理中编辑用户信息 1.2.绑定公司id 1.3.显示在页面 2.修改识别逻辑 2.1.分析 先识别&#xff0c;再判断&#xff0c;清空键把识别结果清空 2.2.写码 修改了发票识别逻辑&#xff0c;略... 3.接高拍仪 3.1.js引入报错 分析&#xff1a; 遇到的错误…

MySQL数据库基础第一篇(SQL通用语法与分类)

文章目录 一、SQL通用语法二、SQL分类三、DDL语句四、DML语句1.案例代码2.读出结果 五、DQL语句1.DQL-基本查询2.DQL-条件查询3.DQL-聚合函数4.DQL-分组查询5.DQL-排序查询6.DQL-分页查询7.DQL语句-执行顺序1.案例代码2.读出结果 六、DCL语句1.DCL-管理用户2.DCL-权限控制1.案例…

鸿蒙开发-UI-页面路由

鸿蒙开发-UI-组件 鸿蒙开发-UI-组件2 鸿蒙开发-UI-组件3 鸿蒙开发-UI-气泡/菜单 文章目录 一、基本概念 二、页面跳转 1.router基本概念 2.使用场景 3.页面跳转参数传递 三、页面返回 1.普通页面返回 2.页面返回前增加一个询问框 1.系统默认询问框 2.自定义询问框 总…

【Mysql】数据库架构学习合集

目录 1. Mysql整体架构1-1. 连接层1-2. 服务层1-3. 存储引擎层1-4. 文件系统层 2. 一条sql语句的执行过程2-1. 数据库连接池的作用2-2. 查询sql的执行过程2-1. 写sql的执行过程 1. Mysql整体架构 客户端&#xff1a; 由各种语言编写的程序&#xff0c;负责与Mysql服务端进行网…

【安装记录】Chrono Engine安装记录

本文仅用于个人安装记录。 官方安装教程 https://api.projectchrono.org/8.0.0/tutorial_install_chrono.html Windows下安装 windows下安装就按照教程好了。采用cmake-gui进行配置&#xff0c;建议首次安装只安装核心模块。然后依此configure下irrlicht&#xff0c;sensor…

J-Link:STM32使用J-LINK烧录程序,其他MCU也通用

说明&#xff1a;本文记录使用J-LINK烧录STM32程序的过程。 1. J-LINK驱动、软件下载 1、首先拥有硬件J-Link烧录器。 2、安装J-Link驱动程序SEGGER 下载地址如下 https://www.segger.com 直接下载就可以了。 2.如何使用J-LINK向STM32烧写程序 1、安装好以后打开J-LINK Fl…

从零开始:CentOS系统下搭建DNS服务器的详细教程

前言 如果你希望在CentOS系统上建立自己的DNS服务器,那么这篇文章绝对是你不容错过的宝藏指南。我们提供了详尽的步骤和实用技巧,让你能够轻松完成搭建过程。从安装必要的软件到配置区域文件,我们都将一一为你呈现。无论你的身份是运维人员,还是程序员,抑或是对网络基础设…

【脑电信号处理与特征提取】P7-涂毅恒:运用机器学习技术和脑电进行大脑解码

运用机器学习技术和脑电进行大脑解码 科学研究中的大脑解码 比如2019年在Nature上一篇文章&#xff0c;来自UCSF的Chang院士的课题组&#xff0c;利用大脑活动解码语言&#xff0c;帮助一些患者恢复语言功能。 大脑解码的重要步骤 大脑解码最重要的两步就是信号采集和信号…

Coremail启动鸿蒙原生应用开发,打造全场景邮件办公新体验

1月18日&#xff0c;华为在深圳举行鸿蒙生态千帆启航仪式&#xff0c;Coremail出席仪式并与华为签署鸿蒙合作协议&#xff0c;宣布正式启动鸿蒙原生应用开发。作为首批拥抱鸿蒙的邮件领域伙伴&#xff0c;Coremail的加入标志着鸿蒙生态版图进一步完善。 Coremail是国内自建邮件…

浏览器——HTTP缓存机制与webpack打包优化

文章目录 概要强缓存定义开启 关闭强缓存协商缓存工作机制通过Last-Modified If-Modified-Since通过ETag If-None-Match 不使用缓存前端利用缓存机制&#xff0c;修改打包方案webpack 打包webpack 打包名称优化webpack 默认的hash 值webapck其他hash 类型配置webpack打包 web…

数据结构-内部排序

简介 排序&#xff08;Sorting&#xff09;&#xff1a;将一个数据元素&#xff08;或记录&#xff09;的任意序列&#xff0c;重新排列成一个按关键字有序的序列 排序算法分为内部排序和外部排序 内部排序&#xff1a;在排序期间数据对象全部存放在内存的排序 外部排序&am…