Jetpack ViewModel LiveData:現代Android架構組件的核心力量

引言

在Android應用開發中,數據管理和界面更新一直是開發者面臨的重大挑戰。傳統的開發方式常常導致Activity和Fragment變得臃腫,難以維護,且無法優雅地處理配置變更(如屏幕旋轉)。Jetpack中的ViewModel和LiveData組件正是為解決這些問題而生,它們共同構成了Android應用架構的核心支柱。本文將深入探討這兩個組件的設計理念、使用方法和最佳實踐。

一、ViewModel:生命周期感知的數據容器

1.1 ViewModel概述

ViewModel是設計用來以生命周期感知的方式存儲和管理界面相關數據的組件。它主要有以下特點:

  • 生命周期感知:自動關聯Activity/Fragment的生命周期

  • 配置變更存活:屏幕旋轉等配置變更時數據不會丟失

  • 界面數據分離:促進關注點分離,減輕UI控制器的負擔

  • 作用域限定:與特定的Activity或Fragment實例關聯

1.2 ViewModel解決的問題

傳統Android開發中常見的問題:

  • 數據丟失:配置變更導致Activity重建,臨時數據丟失

  • 內存泄漏:異步操作持有Activity引用導致泄漏

  • 職責過重:Activity/Fragment同時處理UI邏輯和數據操作

  • 測試困難:UI邏輯與業務邏輯混雜,難以單元測試

1.3 基本使用

添加依賴
dependencies {def lifecycle_version = "2.5.1"implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
}
創建ViewModel
class MyViewModel : ViewModel() {private val _counter = MutableLiveData(0)val counter: LiveData<Int> = _counterfun increment() {_counter.value = (_counter.value ?: 0) + 1}
}
在Activity/Fragment中使用
class MainActivity : AppCompatActivity() {private lateinit var viewModel: MyViewModeloverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)// 獲取ViewModel實例viewModel = ViewModelProvider(this).get(MyViewModel::class.java)// 觀察LiveDataviewModel.counter.observe(this) { count ->textView.text = "Count: $count"}button.setOnClickListener {viewModel.increment()}}
}

1.4 ViewModel生命周期

ViewModel的生命周期與其關聯的UI組件密切相關:

  • 創建:當首次請求ViewModel時(通常在onCreate中)

  • 存活:在整個Activity/Fragment的生命周期中保持存活

  • 銷毀:當關聯的Activity結束或Fragment分離時清除

?

?

二、LiveData:可觀察的數據持有者

2.1 LiveData概述

LiveData是一種可觀察的數據持有者類,具有生命周期感知能力,這意味著它只在Activity、Fragment或Service處于活躍生命周期狀態時才會更新這些組件。

主要特點:

  • 數據可觀察:當數據變化時通知觀察者

  • 生命周期感知:自動管理訂閱,避免內存泄漏

  • 自動取消訂閱:當觀察者處于非活躍狀態時不接收更新

  • 最新數據保證:新觀察者會立即收到最新數據

2.2 LiveData類型

  1. MutableLiveData:可變的LiveData基類

  2. MediatorLiveData:可合并多個LiveData源

  3. Transformations:提供map和switchMap操作符

2.3 基本使用

創建LiveData
class UserViewModel : ViewModel() {private val _user = MutableLiveData<User>()val user: LiveData<User> = _userfun loadUser(userId: String) {viewModelScope.launch {val loadedUser = repository.getUser(userId)_user.postValue(loadedUser)}}
}
觀察LiveData
viewModel.user.observe(viewLifecycleOwner) { user ->// 更新UInameTextView.text = user.nameemailTextView.text = user.email
}

2.4 LiveData轉換

map轉換
val userName: LiveData<String> = Transformations.map(viewModel.user) { user ->"${user.firstName} ${user.lastName}"
}
switchMap轉換
private val userId = MutableLiveData<String>()
val user: LiveData<User> = Transformations.switchMap(userId) { id ->repository.getUser(id)
}

三、ViewModel與LiveData的協同工作

ViewModel和LiveData通常一起使用,形成強大的數據管理組合:

  1. ViewModel:持有和管理與UI相關的數據

  2. LiveData:在ViewModel中暴露可觀察的數據

  3. UI控制器:觀察LiveData并更新界面

這種模式的優勢:

  • 數據持久性:配置變更時數據不會丟失

  • 資源管理:自動取消不必要的更新

  • 測試便利:業務邏輯與UI邏輯分離

  • 響應式UI:數據變化自動反映到界面

四、高級用法與最佳實踐

4.1 共享ViewModel

在Fragment間共享數據:

// 在Activity中獲取
val sharedViewModel = ViewModelProvider(this).get(SharedViewModel::class.java)// 在Fragment中獲取(同一個Activity范圍內)
val sharedViewModel = ViewModelProvider(requireActivity()).get(SharedViewModel::class.java)

4.2 保存狀態

處理進程死亡的情況(結合SavedStateHandle):

class SavedStateViewModel(private val state: SavedStateHandle) : ViewModel() {val counter: LiveData<Int> = state.getLiveData("counter", 0)fun increment() {state["counter"] = (counter.value ?: 0) + 1}
}

4.3 結合Repository模式

class UserViewModel(private val repository: UserRepository,savedStateHandle: SavedStateHandle
) : ViewModel() {private val userId: String = savedStateHandle["userId"] ?: throw IllegalArgumentException("Missing userId")val user: LiveData<User> = repository.getUser(userId).asLiveData()
}

4.4 測試ViewModel

@RunWith(JUnit4::class)
class MyViewModelTest {private lateinit var viewModel: MyViewModel@Beforefun setup() {viewModel = MyViewModel()}@Testfun increment_increasesCounter() {assertEquals(0, viewModel.counter.value)viewModel.increment()assertEquals(1, viewModel.counter.value)}
}

五、常見問題與解決方案

5.1 何時使用ViewModel vs SavedInstanceState

場景ViewModelSavedInstanceState
屏幕旋轉? 保留? 保留
系統殺死進程? 丟失? 保留
大/復雜數據? 適合? 不適合
簡單UI狀態? 適合? 適合

5.2 LiveData vs Flow

特性LiveDataFlow
生命周期感知? 是? 否(需配合lifecycleScope)
多平臺支持? 僅Android? 跨平臺
復雜數據流? 有限? 強大
冷熱流熱流冷流

5.3 避免內存泄漏

最佳實踐組合

隨著Android開發的不斷演進,ViewModel和LiveData仍然是構建健壯、可維護應用的基礎。掌握這些組件不僅能提升應用質量,還能顯著提高開發效率。

  • 不要在ViewModel中持有View/Activity/Fragment引用

  • 使用viewModelScope管理協程,自動取消

  • 對于Android資源,在ViewModel中提供清理方法

    override fun onCleared() {super.onCleared()// 清理資源
    }

    六、與現代架構組件的集成

    6.1 結合Room數據庫

    @Dao
    interface UserDao {@Query("SELECT * FROM user WHERE id = :userId")fun getUser(userId: String): LiveData<User>
    }class UserRepository(private val userDao: UserDao) {fun getUser(userId: String): LiveData<User> {return userDao.getUser(userId)}
    }

    6.2 結合Data Binding

    <layout><data><variable name="viewmodel"type="com.example.MyViewModel" /></data><TextViewandroid:text="@{String.valueOf(viewmodel.counter)}"... />
    </layout>

    6.3 結合Compose

    @Composable
    fun CounterScreen(viewModel: CounterViewModel = viewModel()) {val count by viewModel.counter.observeAsState(0)Column {Text(text = "Count: $count")Button(onClick = { viewModel.increment() }) {Text("Increment")}}
    }

    七、總結

    ViewModel和LiveData作為Android Jetpack架構組件的核心部分,為現代Android應用開發提供了強大而優雅的解決方案:

  • ViewModel

    • 管理界面相關數據

    • 在配置變更時保留數據

    • 減輕UI控制器的負擔

  • LiveData

    • 提供響應式數據流

    • 自動管理生命周期

    • 防止內存泄漏

  • 使用ViewModel保存和管理UI數據

  • 通過LiveData暴露數據給UI

  • 結合Repository模式處理數據源

  • 使用協程處理異步操作

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/92233.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/92233.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/92233.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Python數據分析案例79——基于征信數據開發信貸風控模型

背景 雖然模型基本都是表格數據那一套了&#xff0c;算法都沒什么新鮮點&#xff0c;但是本次數據還是很值得寫個案例的&#xff0c;有征信數據&#xff0c;各種&#xff0c;個人&#xff0c;機構&#xff0c;逾期匯總..... 這么多特征來做機器學習模型應該還不錯。本次帶來&…

板凳-------Mysql cookbook學習 (十二--------3_2)

3.3鏈接表 結構 P79頁 用一個類圖來表示EmployeeNode類的結構&#xff0c;展示其屬性和關系&#xff1a; plaintext ----------------------------------------- | EmployeeNode | ----------------------------------------- | - emp_no: int …

深度學習圖像預處理:統一輸入圖像尺寸方案

在實際訓練中&#xff0c;最常見也最簡單的做法&#xff0c;就是在送入網絡前把所有圖片「變形」到同一個分辨率&#xff08;比如 256256 或 224224&#xff09;&#xff0c;或者先裁剪&#xff0f;填充成同樣大小。具體而言&#xff0c;可以分成以下幾類方案&#xff1a;一、圖…

pytest-log

問題1&#xff1a;我們在運行測試用例的時候如何記錄測試的log&#xff0c;如何使用&#xff1f;問題2&#xff1a;我寫的函數&#xff0c;為了方便log記錄&#xff0c;但是在pytest運行時&#xff0c;會兼容pytest且不會重復記錄&#xff0c;怎么解決&#xff1f;1、pytest有內…

在安卓源碼中添加自定義jar包給源碼中某些模塊使用

一、具體步驟 1. 準備目錄與 Jar 包 在vendor下 創建新的模塊目錄&#xff0c;放入demo.jar 包&#xff1a; demojar/ # 模塊目錄 ├── Android.bp # 編譯配置文件 └── demo.jar 2. 編寫 Android.bp 配置 Android.bp 示例配置&#xff1a; java_import {…

buntu 22.04 上離線安裝Docker 25.0.5(二)

以下有免費的4090云主機提供ubuntu22.04系統的其他入門實踐操作 地址&#xff1a;星宇科技 | GPU服務器 高性能云主機 云服務器-登錄 相關兌換碼星宇社區---4090算力卡免費體驗、共享開發社區-CSDN博客 兌換碼要是過期了&#xff0c;可以私信我獲取最新兌換碼&#xff01;&a…

初探 Web 環境下的 LLM 安全:攻擊原理與風險邊界

文章目錄前言1 什么是大型語言模型&#xff08;LLM&#xff09;&#xff1f;1.1 LLM的核心特征1.2 LLM在Web場景中的典型應用2 LLM攻擊的核心手段&#xff1a;提示注入與權限濫用3 LLM與API集成的安全隱患&#xff1a;工作流中的漏洞節點3.1 LLM-API集成的典型工作流3.2 工作流…

【新手向】PyTorch常用Tensor shape變換方法

【新手向】PyTorch常用Tensor shape變換方法 前言 B站UP主科研水神大隊長的視頻中介紹了“縫合模塊”大法&#xff0c;其中專門強調了“深度學習 玩的就是shape”。受此啟發&#xff0c;專門整理能夠調整tensor形狀的幾個內置函數&#xff0c;方便以后更好地調整PyTorch代碼中的…

React 18 vs Vue3:狀態管理方案深度對比

?? 背景: React有Redux、Zustand、Jotai等方案 Vue有Pinia、Vuex 4.x 如何選擇適合項目的方案? ?? 核心對比: 維度 React (Redux Toolkit) Vue3 (Pinia) 類型安全 ? 需手動配置TS ? 自動類型推導 代碼量 較多(需寫action) 較少(類似Vuex 5) 響應式原理 不可變數據…

UE5網絡聯機函數

Find Sessions Create Session Join Session Destroy Session Steam是p2p直接聯機 一、steam提供的測試用AppId AppId是steam為每一款游戲所設定的獨有標識&#xff0c;每一款要上架steam的游戲都會擁有獨一無二的AppId。不過為了方便開發者測試&#xff0c;steam提供了游…

Spring Boot 監控:AOP vs Filter vs Java Agent

01前言 在 高并發 微服務 中&#xff0c; 傳統 手動埋點&#xff08;System.currentTimeMillis()&#xff09;就像用體溫計量火箭速度——代碼侵入、重復勞動、維護爆炸。 下文是無侵入、高精度、全鏈路 監控 API 耗時&#xff0c;全程不碰業務代碼的方案&#xff01; 02實戰&…

基于Android的電子記賬本系統

博主介紹&#xff1a;java高級開發&#xff0c;從事互聯網行業多年&#xff0c;熟悉各種主流語言&#xff0c;精通java、python、php、爬蟲、web開發&#xff0c;已經做了多年的畢業設計程序開發&#xff0c;開發過上千套畢業設計程序&#xff0c;沒有什么華麗的語言&#xff0…

7月17日日記

結束了數學建模之后的這兩天一直在緊張的復習&#xff0c;但是說實話效率有點低&#xff0c;因為可能覺得自己找到了兩個小時速成課&#xff0c;覺得無所謂了&#xff0c;所以有點放松了。在宿舍杰哥和林雨城卻一直在復習&#xff0c;感覺他們的微積分和線性代數復習的都比我好…

Linux下SPI設備驅動開發

一.SPI協議介紹1.硬件連接介紹引腳含義&#xff1a;DO(MOSI)&#xff1a;Master Output, Slave Input&#xff0c;SPI主控用來發出數據&#xff0c;SPI從設備用來接收數據。DI(MISO)&#xff1a;Master Input, Slave Output&#xff0c;SPI主控用來發出數據&#xff0c;SPI從設…

用Dify構建氣象智能體:從0到1搭建AI工作流實戰指南

作為一名Agent產品經理,我最近在負責氣象智能體的建設項目。傳統氣象服務面臨三大痛點:數據孤島嚴重(氣象局API、衛星云圖、地面觀測站等多源數據格式不一)、響應鏈路長(從數據采集到預警發布需人工介入多個環節)、交互體驗單一(用戶只能被動接收標準化預警,無法個性化…

Android NDK ffmpeg 音視頻開發實戰

文章目錄接入FFmpeg1.下載FFmpeg 源碼2.編譯FFmpeg.so庫異常處理3.自定義FFmpeg交互so庫創建4.配置CMakeLists.txt5.CMakeLists.txt 環境配置6.Native與Java層調用解碼器準備接入FFmpeg 1.下載FFmpeg 源碼 FFmpeg官網地址 2.編譯FFmpeg.so庫 移動 FFmpeg 源碼文件夾至 Andr…

使用 go-redis-entraid 實現 Entra ID 無密鑰認證

1、依賴與安裝 步驟命令說明安裝&#xff08;或升級&#xff09; go-redis v9.9go get github.com/redis/go-redis/v9latestentraid 必須 ≥ 9.9.0安裝 go-redis-entraidgo get github.com/redis/go-redis-entraid自動拉取 transit 依賴 2、認證方式一覽 方式說明創建 Stream…

window上docker安裝RabbitMQ

1、要進http://localhost:15672管理頁面需要安裝management版本2、搜索鏡像并pull3、啟動鏡像時將端口映射出來4、啟動成功&#xff0c;點擊可查看日志詳情&#xff0c;瀏覽器訪問5、直接使用guest/guest登錄會報錯User can only log in via localhost解決辦法有兩個&#xff1…

異世界歷險之數據結構世界(排序(插入,希爾,堆排))

前言 介紹 插入排序 基本知識&#xff1a; 直接插入排序是一種簡單的插入排序法&#xff0c;其基本思想是&#xff1a; 把待排序的記錄按其關鍵碼值的大小逐個插入到一個已經排好序的有序序列中&#xff0c;直到所有的記錄插入完為止&#xff0c;得到一個新的有序序列 直接插入…

oracle 數據庫中,將幾張表的數據按指定日期范圍實時同步至同一個數據庫的備份表中。

以下是一個Oracle數據庫中實現表數據按指定日期范圍實時同步至備份表的解決方案。這個方案使用存儲過程和觸發器組合實現&#xff1a; 1. 創建備份表結構 首先需要為每張需要備份的表創建對應的備份表&#xff0c;結構與原表相同&#xff1a; -- 為原表創建備份表&#xff08;示…