Android 的 ViewModel
是 Jetpack 架構組件庫的核心部分,旨在以生命周期感知的方式存儲和管理與 UI 相關的數據。它的核心目標是解決兩大痛點:
- 數據持久化: 在配置變更(如屏幕旋轉、語言切換、多窗口模式切換)時保留數據,避免重新加載數據造成的資源浪費和用戶體驗中斷。
- 職責分離: 將 UI 控制器(
Activity
/Fragment
)與數據操作邏輯分離,使代碼更清晰、可測試性更強。
核心機制與原理詳解
-
設計目標與核心思想:
- 生命周期感知:
ViewModel
對象的生命周期比其關聯的 UI 控制器(Activity
/Fragment
)更長。它從 UI 控制器創建開始,直到其關聯的 UI 控制器永久銷毀(Activity
finish()
或Fragment
分離且不再附加)時才被銷毀。這意味著配置變更導致的臨時銷毀與重建不會影響ViewModel
。 - 數據持有者: 主要負責持有、準備和管理 UI 所需的數據。它可以執行數據獲取(如從數據庫、網絡)、轉換、聚合等操作。
- UI 控制器解耦: UI 控制器(
Activity
/Fragment
)負責顯示數據和響應用戶交互,ViewModel
負責提供數據和處理業務邏輯。兩者通過觀察(如LiveData
)或直接調用接口進行通信。
- 生命周期感知:
-
創建過程:
- 入口點: 通常使用
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
的構造函數需要一個ViewModelStoreOwner
。ComponentActivity
(AppCompatActivity 的基類) 和Fragment
都實現了這個接口。它提供了訪問ViewModelStore
的能力。 ViewModelProvider
的作用:- 檢查
ViewModelStore
中是否已存在請求類型的ViewModel
實例。 - 如果存在,直接返回該實例。
- 如果不存在,則通過
Factory
(默認為NewInstanceFactory
或AndroidViewModelFactory
)創建新實例,并將其存儲在ViewModelStore
中,然后返回。
- 檢查
- 入口點: 通常使用
-
存儲與作用域 -
ViewModelStore
:- 核心容器: 每個
ViewModelStoreOwner
(如一個Activity
或一個特定Fragment
)內部都維護著一個ViewModelStore
。它是一個簡單的容器類(通常是HashMap<String, ViewModel>
),負責存儲與該作用域關聯的所有ViewModel
實例。 - 鍵 (Key):
ViewModel
在ViewModelStore
中的存儲鍵通常是其類名(如"com.example.MyViewModel"
)。當使用帶Factory
的特定鍵時(如為同一類型創建多個實例),鍵會更復雜。 - 配置變更下的存活: 當配置變更發生時,系統銷毀并重建 UI 控制器 (
Activity
/Fragment
)。但是,系統會將ViewModelStore
對象保留在內存中。重建后的新 UI 控制器實例會重新附加到同一個ViewModelStore
。因此,ViewModelProvider
能在新 UI 控制器中檢索到之前創建的ViewModel
實例。 - 永久銷毀: 當 UI 控制器真正結束其生命周期(用戶按返回鍵、調用
finish()
或Fragment
被永久移除),系統會調用ViewModelStore
的clear()
方法。該方法會遍歷所有存儲的ViewModel
實例,調用它們的onCleared()
方法(用于釋放資源,如取消異步任務),然后清空 Map。之后,ViewModelStore
及其包含的ViewModel
實例會被垃圾回收。
- 核心容器: 每個
-
與生命周期的綁定 -
Lifecycle
:- 自動關聯: 當你通過
ViewModelProvider(owner)
創建ViewModel
時,該ViewModel
就自動與owner
(ViewModelStoreOwner
) 的生命周期關聯起來了。 onCleared()
鉤子:ViewModel
類提供了一個onCleared()
方法。當關聯的ViewModelStore
被clear()
時(即 UI 控制器永久銷毀時),框架會自動調用這個方法。開發者可以重寫此方法來清理資源(如取消正在進行的網絡請求、關閉數據庫連接、移除監聽器等)。這是ViewModel
感知其“結束”生命周期的關鍵點。
- 自動關聯: 當你通過
-
數據通信 (通常結合
LiveData
):- 最佳搭檔: 雖然
ViewModel
可以包含任何數據,但LiveData
是其推薦的用于向 UI 暴露數據的方式。 - 機制:
ViewModel
內部持有LiveData
對象(通常是MutableLiveData
私有,暴露為LiveData
公有)。UI 控制器 (Activity
/Fragment
) 在onCreate()
或onViewCreated()
中觀察這些LiveData
。 - 優勢:
- 生命周期感知訂閱:
LiveData
自動管理訂閱,確保只在 UI 控制器處于活躍狀態 (STARTED
或RESUMED
) 時才更新 UI,避免在后臺更新導致的崩潰或資源浪費。 - 數據更新:
ViewModel
中的業務邏輯(如響應按鈕點擊的網絡請求)完成后,通過更新MutableLiveData
的值來觸發LiveData
通知觀察者(UI 控制器)。 - 配置變更無縫銜接: 由于
ViewModel
和LiveData
在配置變更后存活,新的 UI 控制器重新觀察同一個LiveData
時,會立即收到最后一次保存的數據,從而實現無縫恢復。
- 生命周期感知訂閱:
- 最佳搭檔: 雖然
-
作用域擴展 (
SavedStateHandle
):- 需求: 基本
ViewModel
在進程被系統殺死后重建時,其內部數據也會丟失。需要一種機制在進程死亡后恢復少量關鍵 UI 狀態(如列表滾動位置、輸入框臨時內容)。 - 解決方案:
ViewModel
庫提供了SavedStateHandle
作為ViewModel
構造函數的參數。 - 原理:
- 當使用
SavedStateHandle
時,ViewModel
的創建工廠(如AbstractSavedStateViewModelFactory
)會負責將SavedStateHandle
注入到ViewModel
中。 SavedStateHandle
本質上是一個鍵值對容器 (Map<String, Any?>
)。它利用底層Activity
的onSaveInstanceState(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() 自動處理
- 需求: 基本
-
Fragment 間共享數據:
- 場景: 同一個
Activity
下的多個Fragment
需要共享數據(如購物車、用戶資料)。 - 實現: 讓這些
Fragment
使用 同一個作用域 的ViewModelStoreOwner
。通常,這個共享的作用域就是它們所屬的Activity
。 - 方法: 在
Fragment
中,使用activityViewModels()
委托或ViewModelProvider(requireActivity())
來獲取ViewModel
實例。 - 原理: 所有通過該
Activity
作用域 (ViewModelStoreOwner
) 獲取的同一類型的ViewModel
(使用默認 Key),返回的都是同一個實例。因此,不同的Fragment
訪問的是同一個ViewModel
對象,自然就實現了數據共享和通信。
- 場景: 同一個
-
底層關鍵類與交互:
ViewModel
: 開發者繼承的基類,包含數據和邏輯,有onCleared()
鉤子。ViewModelStoreOwner
: 接口(ComponentActivity
,Fragment
實現),提供getViewModelStore()
方法。ViewModelStore
: 內部維護一個Map<String, ViewModel>
,負責存儲和清理ViewModel
。ViewModelProvider
: 工廠類,負責從ViewModelStore
獲取或創建ViewModel
實例。ViewModelProvider.Factory
: 接口,用于創建ViewModel
實例(支持帶參數構造函數)。SavedStateHandle
: 用于在進程死亡后恢復少量狀態的輔助類。ComponentActivity
: 實現了ViewModelStoreOwner
和HasDefaultViewModelProviderFactory
,在其onRetainNonConfigurationInstance()
中保存ViewModelStore
,在onCreate()
中恢復。在其onDestroy()
中判斷是否為配置變更決定是否調用ViewModelStore.clear()
。FragmentManagerViewModel
: (Fragment 作用域實現的關鍵) 一個特殊的ViewModel
,由FragmentManager
持有,用于管理Fragment
作用域的ViewModelStore
以及嵌套Fragment
的作用域關系。
-
重要注意事項:
- 絕不持有 View/Activity Context 引用:
ViewModel
生命周期可能比Activity
長。如果持有Activity
引用,會導致Activity
無法被回收,造成內存泄漏。如果需要Application Context
,使用AndroidViewModel
(它持有Application
引用,Application
生命周期等同于進程)。 - 輕量級狀態恢復:
SavedStateHandle
用于恢復少量、序列化/反序列化快的 UI 相關狀態。不要用它存儲大量數據或復雜對象。大數據應持久化到數據庫或網絡。 - 異步操作: 在
ViewModel
中啟動的異步操作(如協程、LiveData
轉換),必須在onCleared()
中取消或清理,防止內存泄漏和無效更新。 - 測試友好: 由于
ViewModel
不依賴 Android 框架的具體 UI,它們可以非常方便地在 JUnit 測試中進行單元測試。
- 絕不持有 View/Activity Context 引用:
總結流程圖
(配置變更 / 進程重建)|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 架構基石。理解 ViewModelStoreOwner
、ViewModelStore
和 ViewModelProvider
的協作機制是掌握其底層原理的關鍵。