Android ViewModel機制與底層原理詳解

Android 的 ViewModel 是 Jetpack 架構組件庫的核心部分,旨在以生命周期感知的方式存儲和管理與 UI 相關的數據。它的核心目標是解決兩大痛點:

  1. 數據持久化: 在配置變更(如屏幕旋轉、語言切換、多窗口模式切換)時保留數據,避免重新加載數據造成的資源浪費和用戶體驗中斷。
  2. 職責分離: 將 UI 控制器(Activity/Fragment)與數據操作邏輯分離,使代碼更清晰、可測試性更強。

核心機制與原理詳解

  1. 設計目標與核心思想:

    • 生命周期感知: ViewModel 對象的生命周期比其關聯的 UI 控制器(Activity/Fragment)更長。它從 UI 控制器創建開始,直到其關聯的 UI 控制器永久銷毀Activity finish()Fragment 分離且不再附加)時才被銷毀。這意味著配置變更導致的臨時銷毀與重建不會影響 ViewModel
    • 數據持有者: 主要負責持有、準備和管理 UI 所需的數據。它可以執行數據獲取(如從數據庫、網絡)、轉換、聚合等操作。
    • UI 控制器解耦: UI 控制器(Activity/Fragment)負責顯示數據和響應用戶交互,ViewModel 負責提供數據和處理業務邏輯。兩者通過觀察(如 LiveData)或直接調用接口進行通信。
  2. 創建過程:

    • 入口點: 通常使用 ViewModelProvider 來獲取 ViewModel 實例。
    // 在 Activity 中
    val viewModel = ViewModelProvider(this).get(MyViewModel::class.java)
    // 在 Fragment 中 (推薦使用 activityViewModels 或 viewModels)
    val viewModel: MyViewModel by viewModels()
    val sharedViewModel: SharedViewModel by activityViewModels()
    
    • 關鍵參數 - ViewModelStoreOwner: ViewModelProvider 的構造函數需要一個 ViewModelStoreOwnerComponentActivity (AppCompatActivity 的基類) 和 Fragment 都實現了這個接口。它提供了訪問 ViewModelStore 的能力。
    • ViewModelProvider 的作用:
      • 檢查 ViewModelStore 中是否已存在請求類型的 ViewModel 實例。
      • 如果存在,直接返回該實例。
      • 如果不存在,則通過 Factory(默認為 NewInstanceFactoryAndroidViewModelFactory)創建新實例,并將其存儲在 ViewModelStore 中,然后返回。
  3. 存儲與作用域 - ViewModelStore

    • 核心容器: 每個 ViewModelStoreOwner(如一個 Activity 或一個特定 Fragment)內部都維護著一個 ViewModelStore。它是一個簡單的容器類(通常是 HashMap<String, ViewModel>),負責存儲與該作用域關聯的所有 ViewModel 實例。
    • 鍵 (Key): ViewModelViewModelStore 中的存儲鍵通常是其類名(如 "com.example.MyViewModel")。當使用帶 Factory 的特定鍵時(如為同一類型創建多個實例),鍵會更復雜。
    • 配置變更下的存活: 當配置變更發生時,系統銷毀并重建 UI 控制器 (Activity/Fragment)。但是,系統會將 ViewModelStore 對象保留在內存中。重建后的新 UI 控制器實例會重新附加到同一個 ViewModelStore。因此,ViewModelProvider 能在新 UI 控制器中檢索到之前創建的 ViewModel 實例。
    • 永久銷毀: 當 UI 控制器真正結束其生命周期(用戶按返回鍵、調用 finish()Fragment 被永久移除),系統會調用 ViewModelStoreclear() 方法。該方法會遍歷所有存儲的 ViewModel 實例,調用它們的 onCleared() 方法(用于釋放資源,如取消異步任務),然后清空 Map。之后,ViewModelStore 及其包含的 ViewModel 實例會被垃圾回收。
  4. 與生命周期的綁定 - Lifecycle

    • 自動關聯: 當你通過 ViewModelProvider(owner) 創建 ViewModel 時,該 ViewModel 就自動與 owner (ViewModelStoreOwner) 的生命周期關聯起來了。
    • onCleared() 鉤子: ViewModel 類提供了一個 onCleared() 方法。當關聯的 ViewModelStoreclear() 時(即 UI 控制器永久銷毀時),框架會自動調用這個方法。開發者可以重寫此方法來清理資源(如取消正在進行的網絡請求、關閉數據庫連接、移除監聽器等)。這是 ViewModel 感知其“結束”生命周期的關鍵點。
  5. 數據通信 (通常結合 LiveData):

    • 最佳搭檔: 雖然 ViewModel 可以包含任何數據,但 LiveData 是其推薦的用于向 UI 暴露數據的方式。
    • 機制: ViewModel 內部持有 LiveData 對象(通常是 MutableLiveData 私有,暴露為 LiveData 公有)。UI 控制器 (Activity/Fragment) 在 onCreate()onViewCreated() 中觀察這些 LiveData
    • 優勢:
      • 生命周期感知訂閱: LiveData 自動管理訂閱,確保只在 UI 控制器處于活躍狀態 (STARTEDRESUMED) 時才更新 UI,避免在后臺更新導致的崩潰或資源浪費。
      • 數據更新: ViewModel 中的業務邏輯(如響應按鈕點擊的網絡請求)完成后,通過更新 MutableLiveData 的值來觸發 LiveData 通知觀察者(UI 控制器)。
      • 配置變更無縫銜接: 由于 ViewModelLiveData 在配置變更后存活,新的 UI 控制器重新觀察同一個 LiveData 時,會立即收到最后一次保存的數據,從而實現無縫恢復。
  6. 作用域擴展 (SavedStateHandle):

    • 需求: 基本 ViewModel 在進程被系統殺死后重建時,其內部數據也會丟失。需要一種機制在進程死亡后恢復少量關鍵 UI 狀態(如列表滾動位置、輸入框臨時內容)。
    • 解決方案: ViewModel 庫提供了 SavedStateHandle 作為 ViewModel 構造函數的參數。
    • 原理:
      • 當使用 SavedStateHandle 時,ViewModel 的創建工廠(如 AbstractSavedStateViewModelFactory)會負責將 SavedStateHandle 注入到 ViewModel 中。
      • SavedStateHandle 本質上是一個鍵值對容器 (Map<String, Any?>)。它利用底層 ActivityonSaveInstanceState(Bundle) 機制。
      • 在 UI 控制器臨時銷毀(配置變更)或可能永久銷毀(進程回收)前,SavedStateHandle 中的數據會被序列化到 Bundle 中。
      • 在 UI 控制器重建后,Bundle 中的數據會被反序列化回 SavedStateHandle。這樣,即使在進程被殺死后重建,ViewModel 也能通過 SavedStateHandle 恢復那些關鍵狀態。
    • 使用:
      class MyViewModel(private val savedStateHandle: SavedStateHandle) : ViewModel() {val someState: MutableStateFlow<String> = savedStateHandle.getStateFlow("key", "")// 或者使用 LiveDataval liveDataState: LiveData<String> = savedStateHandle.getLiveData("key")fun updateState(newValue: String) {savedStateHandle["key"] = newValue // 自動觸發保存}
      }
      // 創建時需要使用 SavedStateViewModelFactory 或 by viewModels() 自動處理
      
  7. Fragment 間共享數據:

    • 場景: 同一個 Activity 下的多個 Fragment 需要共享數據(如購物車、用戶資料)。
    • 實現: 讓這些 Fragment 使用 同一個作用域ViewModelStoreOwner。通常,這個共享的作用域就是它們所屬的 Activity
    • 方法:Fragment 中,使用 activityViewModels() 委托或 ViewModelProvider(requireActivity()) 來獲取 ViewModel 實例。
    • 原理: 所有通過該 Activity 作用域 (ViewModelStoreOwner) 獲取的同一類型的 ViewModel(使用默認 Key),返回的都是同一個實例。因此,不同的 Fragment 訪問的是同一個 ViewModel 對象,自然就實現了數據共享和通信。
  8. 底層關鍵類與交互:

    • ViewModel: 開發者繼承的基類,包含數據和邏輯,有 onCleared() 鉤子。
    • ViewModelStoreOwner: 接口(ComponentActivity, Fragment 實現),提供 getViewModelStore() 方法。
    • ViewModelStore: 內部維護一個 Map<String, ViewModel>,負責存儲和清理 ViewModel
    • ViewModelProvider: 工廠類,負責從 ViewModelStore 獲取或創建 ViewModel 實例。
    • ViewModelProvider.Factory: 接口,用于創建 ViewModel 實例(支持帶參數構造函數)。
    • SavedStateHandle: 用于在進程死亡后恢復少量狀態的輔助類。
    • ComponentActivity: 實現了 ViewModelStoreOwnerHasDefaultViewModelProviderFactory,在其 onRetainNonConfigurationInstance() 中保存 ViewModelStore,在 onCreate() 中恢復。在其 onDestroy() 中判斷是否為配置變更決定是否調用 ViewModelStore.clear()
    • FragmentManagerViewModel: (Fragment 作用域實現的關鍵) 一個特殊的 ViewModel,由 FragmentManager 持有,用于管理 Fragment 作用域的 ViewModelStore 以及嵌套 Fragment 的作用域關系。
  9. 重要注意事項:

    • 絕不持有 View/Activity Context 引用: ViewModel 生命周期可能比 Activity 長。如果持有 Activity 引用,會導致 Activity 無法被回收,造成內存泄漏。如果需要 Application Context,使用 AndroidViewModel(它持有 Application 引用,Application 生命周期等同于進程)。
    • 輕量級狀態恢復: SavedStateHandle 用于恢復少量、序列化/反序列化快的 UI 相關狀態。不要用它存儲大量數據或復雜對象。大數據應持久化到數據庫或網絡。
    • 異步操作:ViewModel 中啟動的異步操作(如協程、LiveData 轉換),必須在 onCleared() 中取消或清理,防止內存泄漏和無效更新。
    • 測試友好: 由于 ViewModel 不依賴 Android 框架的具體 UI,它們可以非常方便地在 JUnit 測試中進行單元測試。

總結流程圖

(配置變更 / 進程重建)|v
+-------------------+
| UI Controller      | (Activity/Fragment) 銷毀或重建
| (onDestroy)       | --(永久銷毀?)--> Yes -> [調用 ViewModelStore.clear()] -> [觸發 ViewModel.onCleared()]
|                   | --(配置變更?)--> Yes -> [系統保留 ViewModelStore]
+-------------------+|| (重建后)v
+-------------------+
| UI Controller      | (新的 Activity/Fragment 實例)
| (onCreate)        | --[創建 ViewModelProvider] --> [請求 ViewModel]
+-------------------+|v
+-------------------+
| ViewModelProvider | --[檢查 ViewModelStore] --> [存在?] -> Yes -> 返回現有實例
|                   |                          --> No  -> [使用 Factory 創建新實例] -> [存入 ViewModelStore] -> 返回實例
+-------------------+|v
+-------------------+
| ViewModel         | --[持有 LiveData/SavedStateHandle] --> [提供數據/處理邏輯]
|                   | <--[觀察 LiveData / 調用方法]------- UI Controller
+-------------------+|v
(UI 顯示數據/響應用戶交互)

ViewModel 的核心在于 ViewModelStore 在配置變更中的持久性,以及其生命周期與 UI 控制器的解耦(存活至永久銷毀)。結合 LiveData 的生命周期感知數據觀察和 SavedStateHandle 的輕量級狀態持久化,它構成了 Android 現代、健壯、可測試的 UI 架構基石。理解 ViewModelStoreOwnerViewModelStoreViewModelProvider 的協作機制是掌握其底層原理的關鍵。

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

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

相關文章

雙倍硬件=雙倍性能?TDengine線性擴展能力深度實測驗證!

軟件擴展能力是軟件架構設計中的一個關鍵要素&#xff0c;具有良好擴展能力的軟件能夠充分利用新增的硬件資源。當軟件性能與硬件增加保持同步比例增長時&#xff0c;我們稱這種現象為軟件具有線性擴展能力。要實現這種線性擴展并不簡單&#xff0c;它要求軟件架構精心設計&…

頻繁迭代下完成iOS App應用上架App Store:一次快速交付項目的完整回顧

在一次面向商戶的會員系統App開發中&#xff0c;客戶要求每周至少更新一次版本&#xff0c;涉及功能迭代、UI微調和部分支付方案的更新。團隊使用Flutter進行跨平臺開發&#xff0c;但大部分成員日常都在Windows或Linux環境&#xff0c;只有一臺云Mac用于打包。如何在高頻率發布…

springsecurity03--異常攔截處理(認證異常、權限異常)

目錄 Spingsecurity異常攔截處理 認證異常攔截 權限異常攔截 注冊異常攔截器 設置跨域訪問 Spingsecurity異常攔截處理 認證異常攔截 /*自定義認證異常處理器類*/ Component public class MyAuthenticationExceptionHandler implements AuthenticationEntryPoint {Overr…

企業如何制作網站?網站制作的步驟與流程?

以下是2025年網站制作的綜合指南&#xff0c;涵蓋核心概念、主流技術及實施流程&#xff1a; 一、定義與范疇 網站制作是通過頁面結構設計、程序設計、數據庫開發等技術&#xff0c;將視覺設計轉化為可交互網頁的過程&#xff0c;包含前端展示與后臺功能實現。其核心目標是為企…

Rust+Blender:打造高性能游戲引擎

基于Rust和Blender的游戲引擎 以下是基于Rust和Blender的游戲引擎開發實例,涵蓋不同應用場景和技術方向的實際案例。案例分為工具鏈整合、渲染技術、物理模擬等類別,每個案例附核心代碼片段或實現邏輯。 工具鏈整合案例 案例1:Blender模型導出到Bevy引擎 使用blender-bev…

Git基本操作1

Git 是一款分布式版本控制系統&#xff0c;主要用于高效管理代碼版本和團隊協作開發。它能精確記錄每次代碼修改&#xff0c;支持版本回溯和分支管理&#xff0c;讓開發者可以并行工作而互不干擾。通過本地提交和遠程倉庫同步&#xff0c;Git 既保障了代碼安全&#xff0c;又實…

React Native 組件間通信方式詳解

React Native 組件間通信方式詳解 在 React Native 開發中&#xff0c;組件間通信是核心概念之一。以下是幾種主要的組件通信方式及其適用場景&#xff1a; 簡單父子通信&#xff1a;使用 props 和回調函數兄弟組件通信&#xff1a;提升狀態到共同父組件跨多級組件&#xff1a;…

TCP的可靠傳輸機制

TCP通過校驗和、序列號、確認應答、重發控制、連接管理以及窗口控制等機制實現可靠性的傳輸。 先來看第一個可靠性傳輸的方法。 通過序列號和可靠性提供可靠性 TCP是面向字節的。TCP把應用層交下來的報文&#xff08;可能要劃分為許多較短的報文段&#xff09;看成一個一個字節…

沒有DBA的敏捷開發管理

前言一家人除了我都去旅游了&#xff0c;我這項請假&#xff0c;請不動啊。既然在家了&#xff0c;閑著也是閑著&#xff0c;就復盤下最近的工作&#xff0c;今天就復盤表結構管理吧&#xff0c;隨系統啟動的&#xff0c;不是flyway&#xff0c;而是另一個liquibase&#xff0c…

go-carbon v2.6.10發布,輕量級、語義化、對開發者友好的 golang 時間處理庫

carbon 是一個輕量級、語義化、對開發者友好的 Golang 時間處理庫&#xff0c;提供了對時間穿越、時間差值、時間極值、時間判斷、星座、星座、農歷、儒略日 / 簡化儒略日、波斯歷 / 伊朗歷的支持。 carbon 目前已捐贈給 dromara 開源組織&#xff0c;已被 awesome-go 收錄&am…

【AI News | 20250708】每日AI進展

AI Repos 1、claude-code-templates Claude Code Templates是一款全面的命令行工具&#xff0c;旨在為不同編程語言和框架&#xff08;如JavaScript/TypeScript、Python等&#xff0c;Go和Rust即將推出&#xff09;提供優化的Claude Code配置。它通過交互式設置、自動化鉤子&a…

Nginx源碼安裝+靜態站點部署指南(CentOS 7)

安裝包&#xff1a;可自行前往我的飛書下載 Docs 也可以進入 nginx 官網&#xff0c;下載自己所需適應版本 nginx 開始安裝nginx 1. 創建準備目錄 cd /opt mkdir soft module # 創建軟件包和源碼解壓目錄 2. 安裝依賴環境 yum -y install make zlib zlib-devel gcc-c l…

交換機的核心原理和作用

一、交換機的核心原理交換機是一種用于連接多臺設備的網絡硬件&#xff0c;其核心原理基于二層網絡&#xff08;數據鏈路層&#xff09;的 MAC 地址尋址1. MAC 地址學習與存儲當交換機接收到數據幀時&#xff0c;會讀取幀中的源 MAC 地址&#xff0c;并將該地址與對應的端口號記…

【工具變量】上市公司企業金融強監管數據、資管新規數據(2001-2024年)

數據簡介&#xff1a;參考頂刊《經濟研究》李青原&#xff08;2022&#xff09;老師的做法&#xff0c;Post 為時間虛擬變量&#xff0c;根據資管新規實施的時間&#xff0c;當觀測期為2018 年上半年及之后時&#xff0c;Post 取值1&#xff0c;否則取值0。PreFin 為資管新規實…

CSS Grid與Flexbox布局實戰對比

概述 CSS布局技術在過去幾年經歷了重大變革&#xff0c;從傳統的基于浮動和定位的方法&#xff0c;到現在強大的Flexbox和Grid布局系統。這兩種現代布局方法極大地簡化了復雜界面的開發過程&#xff0c;但它們各自適用于不同的場景。本文將對Flexbox和Grid進行深入比較&#x…

[Pytest][Part 4]多種測試運行方式

實現需求2&#xff1a;有兩種運行測試的方式&#xff1a;通過config配置文件運行&#xff0c;測試只需要修改config配置文件cmdline 運行這里是新建一個config類來存儲所有的測試配置&#xff0c;以后配置有修改的話也只需要修改這個類。根據目前的測試需求&#xff0c;config中…

平衡二叉樹的刪除操作

對于平衡二叉樹的操作應對與考試只需要模擬出過程即可&#xff0c;且他的過程和插入的平衡方法一樣&#xff0c;不一樣的只是對于平衡因子的計算上。接下來我將給出方法①刪除結點&#xff08;方法同“二叉排序樹”&#xff09; ②一路向北找到最小不平衡子樹&#xff0c;找不到…

Spark 4.0的 VariantType 類型優點以及使用分析

背景 本文基于Spark 4.0。 總結 對于半結構化的數據來說&#xff0c;我們一般會有兩種方式進行存儲: 第一種是存儲為JSON String,這種可以保證Schema free&#xff0c;但是在使用的時候得解析為JSON&#xff0c;從而進行運算操作。 第二種是存儲為Struct類型&#xff0c;這種雖…

17-C#封裝,繼承,多態與重載

C#封裝繼承多態 1. 2. 3.多態 public abstract class animal//抽象類 {public abstract void eat();//抽象方法 } public class cat : animal//繼承 {public override void eat()//重寫{messagebox.show("cat eat");} } public class dog: animal//繼承 {public over…

恒創科技:香港站群服務器做seo站群優化效果如何

香港站群服務器做 SEO 站群優化效果如何?在當前搜索引擎優化競爭日益激烈的環境下&#xff0c;越來越多的企業開始關注站群策略這一高效的 SEO 手段。作為亞洲重要的網絡樞紐&#xff0c;香港站群服務器因其獨特優勢&#xff0c;正成為實施 SEO 站群優化的熱門選擇。本文將客觀…