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: CoroutineScope
    get() {
        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: CoroutineContext
        get() = 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!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

VBoxManage 命令行使用

VBoxManage&#xff1a; 序号命令作用1VBoxManage list vms# 查看当前所有虚拟机2VBoxManage list runningvms # 查看当前正在运行的虚拟机3VBoxManage startvm 虚拟机名 --type gui # 启动虚拟机4VBoxManage startvm 虚拟机名 --type headless# 无前端图形界面方式启动虚拟机…

Elasticsearch:构建自定义分析器指南

在本博客中&#xff0c;我们将介绍不同的内置字符过滤器、分词器和分词过滤器&#xff0c;以及如何创建适合我们需求的自定义分析器。更多关于分析器的知识&#xff0c;请详细阅读文章&#xff1a; 开始使用 Elasticsearch &#xff08;3&#xff09; Elasticsearch: analyzer…

Debezium发布历史101

原文地址&#xff1a; https://debezium.io/blog/2021/01/07/debezium-1-4-final-released/ 欢迎关注留言&#xff0c;我是收集整理小能手&#xff0c;工具翻译&#xff0c;仅供参考&#xff0c;笔芯笔芯. Debezium 1.4.0.Final 发布 2021 年 1 月 7 日 作者&#xff1a; 克里…

C#中的WebApi响应Accept头,自动返回xml或者json

Global.asax.cs中的Application_Start方法添加 GlobalConfiguration.Configuration.Formatters.Clear(); GlobalConfiguration.Configuration.Formatters.Add(new XmlMediaTypeFormatter()); GlobalConfiguration.Configuration.Formatters.Add(new JsonMediaTypeFormatter())…

工作七年,对消息推送使用的一些经验和总结

前言&#xff1a;不管是APP还是WEB端都离不开消息推送&#xff0c;尤其是APP端&#xff0c;push消息&#xff0c;小信箱消息&#xff1b;WEB端的代办消息等。因在项目中多次使用消息推送且也是很多项目必不可少的组成部分&#xff0c;故此总结下供自己参考。 一、什么是消息推…

Vue.js 中子组件向父组件传值的方法

Vue.js 是一款流行的 JavaScript 前端框架&#xff0c;它提供了一套完整的工具和 API&#xff0c;使得开发者可以更加高效地构建交互式的 Web 应用程序。其中&#xff0c;组件化是 Vue.js 的一个核心概念&#xff0c;通过组件化可以将一个复杂的应用程序拆分成多个独立的部分&a…

3D应用开发平台HOOPS Platforms优化制造流程和数字化转型

Tech Soft 3D公司的HOOPS Platform &#xff08;包括HOOPS Native Platform 和HOOPS Web Platform&#xff09;&#xff0c;是一种用于开发顶级3D软件的集成技术。具有高性能3D图形&#xff0c;准确&#xff0c;快速的CAD数据转换&#xff0c;3D数据发布以及与流行的建模内核的…

iOS_Xcode_LLDB调试常用命令

文章目录 结构常用命令&#xff1a;1、流程控制&#xff1a;2、常用命令3、进程信息&#xff1a;4、寄存器&#xff1a;register5、镜像&#xff1a;image6、内存&#xff1a;memory7、符号断点&#xff1a;breakpoint8、内存断点&#xff1a;watchpoint9、Tips&#xff1a; 结…

音视频数字化(音频数字化)

在音视频领域,人们始终追求无限还原现场效果,因此音频越逼真越好,视频越清晰越好。之所以我们需要将音视频信号由模拟转为数字,目的是在录制、存储、编辑、复制、回放等环节的不失真,尽量保持原有细节,不因以上操作,导致音画的质量下降。 为此,视频系统分辨率越来越高,…

【iOS ARKit】手动配置环境探头

在上节中我们已经了解了环境探头以及如何使用自动环境探头&#xff0c;这节一起了解如何使用手动配置环境探头。 在使用自动环境反射时&#xff0c;开发人员无须进行有关环境反射的任何操作&#xff0c;只需要设置自动环境反射即可&#xff0c;其余工作完全由 RealityKit 自动完…

ArcGIS Pro字段编号相关代码

字段属于SHP文件的重要组成部分&#xff0c;在某些时候需要对字段进行编号&#xff0c;这里为大家介绍一下字段编号相关的代码&#xff0c;希望能对你有所帮助。 数据来源 教程所使用的数据是从水经微图中下载的POI数据&#xff0c;除了POI数据&#xff0c;常见的GIS数据都可…

全面掌握Django的web框架Django Rest_Framework(一)

文章目录 Django Rest_Framework1. DRF介绍2.DRF特点3.环境安装与配置&#xff08;1&#xff09;DRF需要以下依赖&#xff08;2&#xff09;创建django项目 4.序列化器的使用&#xff08;1&#xff09;创建序列化器 5. 反序列化器使用 Django Rest_Framework 1. DRF介绍 Djan…

springboot141夕阳红公寓管理系统的设计与实现

基于Spring Boot的夕阳红公寓管理系统的设计与实现 摘 要 如今社会上各行各业&#xff0c;都在用属于自己专用的软件来进行工作&#xff0c;互联网发展到这个时候&#xff0c;人们已经发现离不开了互联网。互联网的发展&#xff0c;离不开一些新的技术&#xff0c;而新技术的…

【爬虫专区】批量下载PDF (无反爬)

天命&#xff1a;只要没反爬&#xff0c;一切都简单 这次爬取的是绿盟的威胁情报的PDF 先看一下结构&#xff0c;很明显就是一个for循环渲染 burp抓包会发现第二次接口请求 接口请求一次就能获取到了所有的数据 然后一个循环批量下载数据即可&#xff0c;其实没啥难度的 imp…

使用Postman做API自动化测试

Postman最基本的功能用来重放请求&#xff0c;并且配合良好的response格式化工具。 高级点的用法可以使用Postman生成各个语言的脚本&#xff0c;还可以抓包&#xff0c;认证&#xff0c;传输文件。 仅仅做到这些还不能够满足一个系统的开发&#xff0c;或者说过于琐碎&#…

【鸿蒙】大模型对话应用(三):跨Ability跳转页面

Demo介绍 本demo对接阿里云和百度的大模型API&#xff0c;实现一个简单的对话应用。 DecEco Studio版本&#xff1a;DevEco Studio 3.1.1 Release HarmonyOS SDK版本&#xff1a;API9 关键点&#xff1a;ArkTS、ArkUI、UIAbility、网络http请求、列表布局、层叠布局 页面跳…

谷歌seo如何发布外链?

在谷歌SEO中发布外链就像是在网络世界中搭建桥梁&#xff0c;你需要在别人的网站里上精心放置通往你网站的路径&#xff0c;这种路径一般是单向的&#xff0c;可能只使用一次&#xff0c;但这依然是个需要花心思的工作 而对于谷歌seo的外链&#xff0c;很多人都会有一个误解&am…

pnpm : 无法加载文件 D:\tool\nvm\nvm\node_global\pnpm.ps1,因为在此系统上禁止运行脚本

你们好&#xff0c;我是金金金。 场景 新创建的项目&#xff0c;在vscode编辑器终端输入 pnpm i&#xff0c;显示报错如上 解决 在终端输入get-ExecutionPolicy(查看执行策略/权限) 输出Restricted(受限的) 终端再次输入Set-ExecutionPolicy -Scope CurrentUser命令给用户赋予…

Oracle篇—普通表迁移到分区表(第五篇,总共五篇)

☘️博主介绍☘️&#xff1a; ✨又是一天没白过&#xff0c;我是奈斯&#xff0c;DBA一名✨ ✌✌️擅长Oracle、MySQL、SQLserver、Linux&#xff0c;也在积极的扩展IT方向的其他知识面✌✌️ ❣️❣️❣️大佬们都喜欢静静的看文章&#xff0c;并且也会默默的点赞收藏加关注❣…

【乳腺肿瘤诊断分类及预测】基于PNN概率神经网络

课题名称&#xff1a;基于PNN的乳腺肿瘤诊断分类及预测 版本日期&#xff1a;2023-06-15 运行方式: 直接运行PNN0501.m 文件即可 代码获取方式&#xff1a;私信博主或QQ&#xff1a;491052175 模型描述&#xff1a; 威斯康辛大学医学院经过多年的收集和整理&#xff0c;建…