LiveData 和 MutableLiveData 的区别

LiveDataMutableLiveData 的区别 主要在于是否可以修改数据,但它们的工作原理基本相同。下面我们深入对比它们的行为、特性,以及它们在 ViewModelUI 层中的使用方式。


1. LiveDataMutableLiveData 的基本区别

特性LiveDataMutableLiveData
可读 / 可写只读ViewModel 之外无法修改数据)可读可写(可以 setValue()postValue()
修改数据的方法❌ 不能修改setValue(value)postValue(value)
线程安全性线程安全(UI 层只能观察,不会修改)不一定线程安全setValue() 只能在主线程调用)
通知机制观察 LiveDataObserver 只有在活跃状态时才会收到通知同样的通知机制
最佳用途适用于 UI 层,作为 ViewModel 对外暴露的数据适用于 ViewModel 内部管理数据

2. LiveDataMutableLiveData 的使用方式

LiveData 适用于 UI 层

LiveData 主要用作 ViewModel 对外暴露的数据,确保 UI 只能观察,而不能修改,避免 UI 直接篡改数据

class MyViewModel : ViewModel() {// `_count` 是 `MutableLiveData`,ViewModel 内部可以修改private val _count = MutableLiveData(0)// `count` 是 `LiveData`,UI 层只能观察,不能修改val count: LiveData<Int> = _countfun increment() {_count.value = (_count.value ?: 0) + 1}
}
🔹 UI 层只能观察,不能修改
viewModel.count.observe(this) { value ->textView.text = "计数: $value"
}

UI 层无法写入 count.value = 10,只能调用 ViewModelincrement() 方法修改数据,这样 可以保证数据的安全性


MutableLiveData 适用于 ViewModel 内部

MutableLiveData 允许 ViewModel 内部修改数据:

class MyViewModel : ViewModel() {private val _name = MutableLiveData("默认名称")val name: LiveData<String> = _namefun updateName(newName: String) {_name.value = newName  // 这里可以修改数据}
}

在 UI 层:

viewModel.name.observe(this) { newName ->textView.text = newName
}

但 UI 无法直接修改 name,只能通过 ViewModel 提供的 updateName() 方法进行修改。


3. setValue() vs postValue()

setValue(value)

  • 必须在主线程调用
  • 立即更新值并通知观察者
  • 适用于 UI 线程内的操作

示例:

_liveData.value = 100  // 立即更新 UI

postValue(value)

  • 可以在后台线程调用(内部是 Handler.post() 机制)。
  • 不会立即通知观察者,而是放入消息队列,等主线程有空时再更新 UI。
  • 适用于异步任务更新数据

示例:

Thread {_liveData.postValue(200)  // 在子线程更新数据
}.start()

ViewModel 中,通常会:

fun fetchData() {viewModelScope.launch(Dispatchers.IO) {val data = repository.getData()_liveData.postValue(data)  // 在后台线程更新数据}
}

4. 为什么 LiveData 可能不会触发通知?

LiveData 只有在 活跃状态(Lifecycle.State.STARTEDRESUMED 才会通知观察者,这就是为什么 MediatorLiveData 可能无法监听到 LiveData 的变更

val liveData = MutableLiveData(1)// 观察者 1
liveData.observe(owner1) { value ->Log.d("Observer1", "收到数据: $value")
}// 观察者 2
liveData.observe(owner2) { value ->Log.d("Observer2", "收到数据: $value")
}

情况 1owner1 处于 RESUMED 状态,owner2 处于 PAUSED 状态:

  • 只有 owner1 会收到数据变化通知

情况 2:如果 owner2 变成 RESUMED 状态,它会 自动收到最新的数据,但不会收到 历史变更

情况 3:如果 LiveData 没有改变值,而是设置相同值:

liveData.value = 1  // 原值就是 1,不会触发通知

这时 LiveData 不会通知任何观察者,因为值没有变化!


5. 总结

特性LiveDataMutableLiveData
是否可写❌ 只能观察,不能修改✅ 可读可写
最佳用途作为 ViewModel 对外暴露的变量,UI 只能观察ViewModel 内部管理数据
线程安全性线程安全,UI 只能观察可能需要 postValue() 处理异步修改
通知机制只有 活跃 Observer 才会收到通知LiveData
数据变化通知如果值相同,不会触发通知LiveData

最佳实践

  • LiveData 适用于 UI 监听数据,避免 UI 直接修改数据。
  • MutableLiveData 适用于 ViewModel 内部维护数据,并且 UI 只能通过 ViewModel 提供的方法修改数据。
  • postValue() 适用于后台线程更新 LiveData,否则 只能在主线程使用 setValue()

这样能 确保数据管理的清晰性,避免 UI 直接修改数据,提高可维护性

二 使用private val _xxx的好处

ViewModel 中这样写:

private val _selectedModePosition = MutableLiveData(0)
val selectedModePosition: LiveData<Int> = _selectedModePosition

是一种 最佳实践,主要是为了 封装数据,确保数据安全性,并且 符合 MVVM 架构原则。我们详细分析其中的作用和好处:


1. 保护数据,防止 UI 直接修改

如果 ViewModel 直接暴露 MutableLiveData,UI 层(ActivityFragment)可以随意修改数据,比如:

viewModel.selectedModePosition.value = 2 // 直接修改数据(不安全)

这样 可能会导致数据不一致,甚至会破坏 ViewModel 内部的业务逻辑。

LiveData只读的,所以这样写:

private val _selectedModePosition = MutableLiveData(0)
val selectedModePosition: LiveData<Int> = _selectedModePosition

UI 只能观察 selectedModePosition,但不能直接修改:

viewModel.selectedModePosition.observe(this) { mode ->// 只读,不能修改
}

想修改数据?必须调用 ViewModel 提供的方法:

fun updateMode(newMode: Int) {_selectedModePosition.value = newMode
}

这样 UI 只能这样更新:

viewModel.updateMode(1) // 只能通过 ViewModel 逻辑更新数据

保证数据的完整性,不会被外部随意修改!


2. 符合 MVVM 架构,确保单一数据源

MVVM 结构中,ViewModel 负责管理数据,View 只负责显示:

  • ViewModel 负责更新 _selectedModePosition
  • UI 只能观察 selectedModePosition

如果 ViewModel 直接暴露 MutableLiveData,UI 可以随意改动数据,破坏数据流动的单向性:

viewModel._selectedModePosition.value = 1 // 直接改动数据,不安全 ❌

如果多个地方都能改 MutableLiveData,就可能导致:

  • 数据被意外篡改
  • 不同组件的数据状态不一致
  • 数据来源不清晰,难以维护

所以,我们封装 MutableLiveData,让 UI 只能通过 ViewModel 控制数据,确保所有数据变化都从 ViewModel 统一管理。


3. 提高代码的可维护性

封装 MutableLiveDataViewModel 统一管理数据,有助于:

  • 方便调试,所有数据修改都必须经过 ViewModel
  • 减少 Bug,不会有 UI 直接篡改数据的风险
  • 提升可读性,让数据流更清晰

如果所有 LiveData 都暴露给 UI,维护起来会很混乱:

viewModel.someData.value = 100 // UI 直接修改,难以追踪 ❌

当出现 Bug 时,很难知道 是谁修改了这个数据

封装后:

fun updateMode(newMode: Int) {_selectedModePosition.value = newMode
}

可以很清楚地看到:

  • 数据只能从 ViewModel 改变
  • 其他地方不会直接修改 MutableLiveData
  • 容易找到数据更新的来源

4. 便于扩展(如果需要 Transformations)

假设以后我们要基于 selectedModePosition 计算一个新的 LiveData,可以这样:

val modeDescription: LiveData<String> = Transformations.map(selectedModePosition) { mode ->when (mode) {0 -> "通风模式"1 -> "加热模式"2 -> "按摩模式"else -> "未知模式"}
}

由于 selectedModePositionLiveData,它可以作为 Transformations.map() 的输入,这样我们可以创建一个新的 LiveData,不会影响原始数据。

如果 ViewModel 直接暴露 MutableLiveDataTransformations.map() 可能会变得复杂,而且 UI 也可能直接修改 MutableLiveData,破坏数据逻辑。


总结

为什么这样写?好处
_selectedModePosition 只允许 ViewModel 修改防止 UI 直接修改数据,确保数据安全
UI 只能读取 selectedModePosition符合 MVVM 设计,数据单向流动
ViewModel 统一管理数据便于调试,减少 Bug,代码更清晰
便于扩展,例如 Transformations可以轻松派生新的 LiveData

所以,在 ViewModel 里封装 MutableLiveData最佳实践,这样可以:
✅ 保护数据
✅ 确保 MVVM 架构清晰
✅ 方便维护和调试

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

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

相关文章

SDK中窗口调用

存在窗口A和B的win32程序 , 当点击窗口A中的按钮后会弹出窗口B #include <windows.h>// 窗口 B 的窗口过程 LRESULT CALLBACK WindowProcB(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {switch (uMsg) {case WM_DESTROY:PostQuitMessage(0);break;default:ret…

进行性核上性麻痹:饮食调理为健康护航

进行性核上性麻痹是一种复杂的神经退行性疾病&#xff0c;目前虽无法根治&#xff0c;但合理的健康饮食有助于缓解症状、提高患者生活质量。 高蛋白质食物在患者饮食中占据重要地位。鱼肉&#xff0c;尤其是富含 Omega-3 脂肪酸的三文鱼、鳕鱼等&#xff0c;不仅蛋白质含量丰富…

【Windows+Cursor】从0到1配置Arxiv MCP Server,实现论文自主查询、下载、分析、综述生成

1. 安装UV Installation | uv powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex" 将安装路径添加到环境变量 C:\Users\xxxxxx\.local\bin 2. git clone 代码 git clone https://github.com/blazickjp/arxiv-mcp-server.git…

WPF 教程:给 TreeView 添加 SelectedItem 双向绑定支持(MVVM-Friendly)

&#x1f332;WPF 教程&#xff1a;给 TreeView 添加 SelectedItem 双向绑定支持&#xff08;MVVM-Friendly&#xff09; 在 WPF 的 MVVM 应用中&#xff0c;TreeView 是非常常见的控件&#xff0c;但它有个“顽固”的缺陷&#xff1a; ❗它的 SelectedItem 不是依赖属性&…

Linux环境下内存错误问题排查与修复

最近这几天服务器总是掉线&#xff0c;要查一下服务器的问题。可以首先查看一下计算机硬件&#xff0c;这是一台某鱼上拼凑的服务器&#xff1a; sudo lshw -shortH/W path Device Class Description system NF5270M3 (To be filled by O…

函数和模式化——python

一、模块和包 将一段代码保存为应该扩展名为.py 的文件&#xff0c;该文件就是模块。Python中的模块分为三种&#xff0c;分别为&#xff1a;内置模块、第三方模块和自定义模块。 内置模块和第三方模块又称为库内置模块&#xff0c;有 python 解释器自带&#xff0c;不用单独安…

windows下载安装远程桌面工具RealVNC-Server教程(RealVNC_E4_6_1版带注册码)

文章目录 前言一、下载安装包二、安装步骤三、使用VNC-Viewer客户端远程连接&#xff0c;输入ip地址&#xff0c;密码完成连接 前言 在现代工作和生活中&#xff0c;远程控制软件为我们带来了极大的便利。RealVNC - Server 是一款功能强大的远程控制服务器软件&#xff0c;通过…

Android Dagger 2 框架的注解模块深入剖析 (一)

本人掘金号&#xff0c;欢迎点击关注&#xff1a;https://juejin.cn/user/4406498335701950 一、引言 在 Android 开发中&#xff0c;依赖注入&#xff08;Dependency Injection&#xff0c;简称 DI&#xff09;是一种强大的设计模式&#xff0c;它能够有效降低代码的耦合度&…

HTML语言的空值合并

HTML语言的空值合并 引言 在现代Web开发中&#xff0c;HTML&#xff08;超文本标记语言&#xff09;是构建网页的基础语言。随着前端技术的快速发展&#xff0c;开发者们面临着大量不同的工具和技术&#xff0c;尤其是在数据处理和用户交互方面。空值合并是一些编程语言中常用…

【数据结构】树的介绍

目录 一、树1.1什么是树&#xff1f;1.2 树的概念与结构1.3树的相关术语1.4 树形结构实际运用场景 二、二叉树2.1 概念与结构2.2 特殊的二叉树2.2.1 满二叉树2.2.2 完全二叉树 个人主页&#xff0c;点击这里~ 数据结构专栏&#xff0c;点击这里~ 一、树 1.1什么是树&#xff1…

Muduo网络库实现 [十三] - HttpRequest模块

目录 设计思路 成员设计 模块实现 设计思路 首先我们要先知道HTTP的请求的流程是什么样子的&#xff0c;不然我们会学的很迷糊。对于HTTP请求如何到来以及去往哪里&#xff0c;我们应该很清楚的知道 HTTP请求在服务器系统中的传递流程是一个多层次的过程: 客户端发起请求…

6. RabbitMQ 死信队列的详细操作编写

6. RabbitMQ 死信队列的详细操作编写 文章目录 6. RabbitMQ 死信队列的详细操作编写1. 死信的概念2. 消息 TTL 过期(触发死信队列)3. 队列超过队列的最大长度(触发死信队列)4. 消息被拒(触发死信队列)5. 最后&#xff1a; 1. 死信的概念 先从概念上解释上搞清楚这个定义&#…

如何使用Selenium进行自动化测试?

&#x1f345; 点击文末小卡片 &#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 对于很多刚入门的测试新手来说&#xff0c;大家都将自动化测试作为自己职业发展的一个主要阶段。可是&#xff0c;在成为一名合格的自动化测试工程师之前&#…

洛谷题单3-P5724 【深基4.习5】求极差 最大跨度值 最大值和最小值的差-python-流程图重构

题目描述 给出 n n n 和 n n n 个整数 a i a_i ai​&#xff0c;求这 n n n 个整数中的极差是什么。极差的意思是一组数中的最大值减去最小值的差。 输入格式 第一行输入一个正整数 n n n&#xff0c;表示整数个数。 第二行输入 n n n 个整数 a 1 , a 2 … a n a_1,…

STM32智能手表——任务线程部分

RTOS和LVGL我没学过&#xff0c;但是应该能硬啃这个项目例程 ├─Application/User/Tasks # 用于存放任务线程的函数 │ ├─user_TaskInit.c # 初始化任务 │ ├─user_HardwareInitTask.c # 硬件初始化任务 │ ├─user_RunModeTasks.c…

ubuntu22.04LTS设置中文输入法

打开搜狗网址直接下载软件&#xff0c;软件下载完成后&#xff0c;会弹出安装教程说明书。 网址:搜狗输入法linux-首页搜狗输入法for linux—支持全拼、简拼、模糊音、云输入、皮肤、中英混输https://shurufa.sogou.com/linux

SQL Server数据库异常-[SqlException (0x80131904): 执行超时已过期] 操作超时问题及数据库日志已满的解决方案

&#x1f9d1; 博主简介&#xff1a;CSDN博客专家、CSDN平台优质创作者&#xff0c;获得2024年博客之星荣誉证书&#xff0c;高级开发工程师&#xff0c;数学专业&#xff0c;拥有高级工程师证书&#xff1b;擅长C/C、C#等开发语言&#xff0c;熟悉Java常用开发技术&#xff0c…

php8 ?-> nullsafe 操作符 使用教程

简介 PHP 8 引入了 ?->&#xff08;Nullsafe 操作符&#xff09;&#xff0c;用于简化 null 检查&#xff0c;减少繁琐的 if 语句或 isset() 代码&#xff0c;提高可读性。 ?-> Nullsafe 操作符的作用 在 PHP 7 及以下&#xff0c;访问对象的属性或方法时&#xff0…

WORD+VISIO输出PDF图片提高清晰度的方法

WORDVISIO输出PDF图片提高清晰度的方法 part 1: visio 绘图part 2: word 导出 part 1: visio 绘图 先在visio中把图片和对应的文字调整为适合插入到文章中的尺寸&#xff1b; 在visio中把所有元素进行组合&#xff1b; 把组合后的图片长和宽等比例放缩&#xff0c;如放大10倍…

重要头文件下的函数

1、<cctype> #include<cctype>加入这个头文件就可以调用以下函数&#xff1a; 1、isalpha(x) 判断x是否为字母 isalpha 2、isdigit(x) 判断x是否为数字 isdigit 3、islower(x) 判断x是否为小写字母 islower 4、isupper(x) 判断x是否为大写字母 isupper 5、isa…