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.pswp.cn/pingmian/75605.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/75605.shtml
英文地址,請注明出處:http://en.pswp.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…