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

news2024/12/25 10:44:22

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!

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

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

相关文章

PHP的线程安全与非线程安全模式选哪个

曾经初学PHP的时候也很困惑对线程安全与非线程安全模式这块环境的选择&#xff0c;也未能理解其中意。近来无意中看到一个教程对线程安全&#xff08;饿汉式&#xff09;&#xff0c;非线程安全&#xff08;懒汉式&#xff09;的描述&#xff0c;虽然觉得现在已经能够很明了透彻…

Python算法题集_最大子数组和

本文为Python算法题集之一的代码示例 题目53&#xff1a;最大子数组和 说明&#xff1a;给你一个整数数组 nums &#xff0c;请你找出一个具有最大和的连续子数组&#xff08;子数组最少包含一个元素&#xff09;&#xff0c;返回其最大和。 子数组 是数组中的一个连续部分。…

RS485自动收发电路震荡的问题

电路 设计初衷 电源5V 选择5V的原因&#xff0c;差分2.5V比1.5V可以提高传输能力 TTL输入 3.3V电平满足需求 TTL输出 4.5V了&#xff0c;MCU是3.3V平台 这样就分为两种情况 MCU接收端可以容忍5V输入 MCU接收端不可以容忍5V输入&#xff0c;就要进行电压转换&#xff0c;我这里使…

【C++】STL优先级队列(priority_queue)

priority_queue 基本介绍 priority_queue就是优先级队列。其头文件就是queue&#xff0c;但是队列和优先级队列关系不大&#xff0c;两个是不同的数据结构。但二者都是适配器&#xff0c;容器适配器。 优先级队列中存放的数据是有优先级的。 其内部有以下成员方法&#xff0c…

跟着cherno手搓游戏引擎【17】整理代码、timestep和transform

这里就不分部走了&#xff0c;直接上代码。 全局&#xff1a; YOTO.h:添加renderer的头文件&#xff1a; #pragma once//用于YOTO APP#include "YOTO/Application.h" #include"YOTO/Layer.h" #include "YOTO/Log.h"#include"YOTO/Core/T…

V2X,启动高增长引擎

车载通讯的下一个新周期&#xff0c;毋庸置疑是V2X。从4G、5G再到C-V2X&#xff0c;是车载通讯逐步从信息娱乐、行车数据监控到万物互联的关键。 去年5月&#xff0c;全球车载通讯芯片巨头—高通公司宣布&#xff0c;与以色列车联网&#xff08;V2X&#xff09;芯片设计公司Aut…

DevOps落地笔记-05|非功能需求:如何有效关注非功能需求

上一讲主要介绍了看板方法以及如何使用看板方法来解决软件研发过程中出现的团队过载、工作不均、任务延期等问题。通过学习前面几个课时介绍的知识&#xff0c;你的团队开始源源不断地交付用户价值。用户对交付的功能非常满意&#xff0c;但等到系统上线后经常出现服务不可用的…

扣库的函数经验

有的库确实可以提出来的 比如这个库 GitHub - intel/x86-simd-sort: C template library for high performance SIMD based sorting algorithms 根据自己的需要是可以&#xff0c;把内容抠出来的&#xff0c;重新build的。 我就自己新建了一个vs的工程&#xff0c;然后把源…

将java对象转换为json字符串的几种常用方法

目录 1.关于json 2.实现方式 1.Gson 2.jackson 3.fastjson 3.与前端的联系 1.关于json JSON是一种轻量级的数据交换格式。它由Douglas Crockford在2001年创造。JSON的全称是JavaScript Object Notation&#xff0c;它是一种文本格式&#xff0c;可以轻松地在各种平台之间传…

【GEE】基于GEE可视化和下载Landsat8 L2A数据(镶嵌、裁剪)

之前发过一篇使用GEE下载Landsat8的文章&#xff0c;然后有很多小伙伴私信我各种问题&#xff0c;如L1C、L2数据代码怎么修改&#xff0c;如何镶嵌&#xff0c;如何去云、 如何裁剪等一系列问题。正好快过年了&#xff0c;手头的事也没有多少了&#xff0c;所以这两天整理了一下…

考研高数(数列极限之f(xn)和{xn}的关系)

说白了&#xff0c;f(xn)是复合函数&#xff0c;是f(x)与{xn}的复合函数。&#xff08;复合函数的单调性有同增异减的原则&#xff09; 例题1&#xff1a;【可以用同增异减的原则&#xff0c;迅速解答&#xff08;有不合理的地方&#xff0c;请各位大佬指正&#xff01;&#…

【操作系统】FileOutputStream的flush操作有时不生效

按照我们的理解&#xff1a;FileOutputStream的flush()方法的作用就是将缓冲区中的数据立即写入到文件中&#xff0c;即使缓冲区没有填满。这样可以确保数据的及时写入&#xff0c;而不需要等待缓冲区填满或者调用 close() 方法关闭流时才写入。真的是这样吗&#xff1f;&#…

dv和ov通配符SSL证书的区别

SSL数字证书是一种数字证书&#xff0c;可以保护网站传输数据安全以及对服务器身份进行验证&#xff0c;SSL证书有很多种&#xff0c;而通配符证书则是其中的一种特殊类型。SSL证书旗下的通配符SSL证书随着互联网的发展&#xff0c;颁发量也越来越多&#xff0c;为了使用户选择…

websocket编写聊天室

【黑马程序员】WebSocket打造在线聊天室【配套资料源码】 总时长 02:45:00 共6P 此文章包含第1p-第p6的内容 简介 温馨提示&#xff1a;现在都是第三方支持聊天&#xff0c;如极光&#xff0c;学这个用于自己项目完全没问题&#xff0c;大项目不建议使用 需求分析 代码

Celeryconfig配置文件

Celery配置文件 本篇介绍Celery配置文件相关&#xff0c;celeryconfig.py Celeryconfig.py 在上篇celery基础用法中&#xff0c;是这样使用celery的实例化的。 # tasks.py 文件名需与实例化对象第一个参数一致 import time from celery import Celeryredis_url redis://Pa…

重磅!讯飞星火V3.5正式发布,3大核心能力超GPT-4 Turbo!

1月30日&#xff0c;科大讯飞召开星火认知大模型V3.5升级发布会&#xff0c;这是国内首个基于全国产算力训练的多模态认知大模型。科大讯飞董事长刘庆峰先生、研究院院长刘聪先生出席了大会&#xff0c;并对最新产品进行了多维度解读。 讯飞星火V3.5的7大核心能力实现全面大幅…

用的到的linux-文件移动-Day2

前言&#xff1a; 在上一节&#xff0c;我们复习了cd大法和创建生成文件和文件夹的方法&#xff0c;介绍了一些“偷懒”&#xff08;高效&#xff09;的小技巧&#xff0c;本节&#xff0c;我们一起来探讨下&#xff0c;我们对文件移动操作时有哪些可以偷懒的小技巧~ 一、复制…

Oracle 集群】RAC知识图文详细教程(四)--缓存融合技术和主要后台进程

Cache Fusion 原理 前面已经介绍了 RAC 的后台进程&#xff0c;为了更深入的了解这些后台进程的工作原理&#xff0c;先了解一下 RAC 中多节点对共享数据文件访问的管理是如何进行的。要了解 RAC 工作原理的中心&#xff0c;需要知道 Cache Fusion 这个重要的概念&#xff0c;要…

基于腾讯云服务器搭建幻兽帕鲁服务器保姆级教程

随着网络游戏的普及&#xff0c;越来越多的玩家希望能够拥有自己的游戏服务器&#xff0c;以便能够自由地玩耍。而腾讯云服务器作为一个优秀的云计算平台&#xff0c;为玩家们提供了一个便捷、稳定、安全的游戏服务器解决方案。本文将为大家介绍如何基于腾讯云服务器搭建幻兽帕…

三、C++中的Mat对象

图片在C中是作为矩阵Matrix进行处理对待的&#xff0c;通过Mat数据类型进行处理 新建项目这里就不再赘述了哈&#xff0c;可以参考博文&#xff1a;零、环境搭建(第三部分Visula Studio中新建项目) 我这边创建的项目名称为&#xff1a;1_31_matrix 为了养成良好的项目开发习惯…