ViewModel的基本使用
1. ViewModel 的基本工作方式
// ViewModelStore 就像餐厅的管理系统
class ViewModelStore {
// 存储所有 ViewModel,像是员工管理系统
private val map = mutableMapOf<String, ViewModel>()
// 添加 ViewModel
fun put(key: String, viewModel: ViewModel) {
map[key] = viewModel
}
// 获取 ViewModel
fun get(key: String): ViewModel? = map[key]
// 清理所有 ViewModel,像是下班时的清场
fun clear() {
map.values.forEach { it.clear() }
map.clear()
}
}
就像:
- 餐厅有个管理系统(ViewModelStore)
- 记录所有在职员工(ViewModel)
- 即使换班(屏幕旋转),员工还在工作
- 只有餐厅关门(Activity 销毁)才会清理
2. ViewModel 的创建过程
// ViewModelProvider 就像是人事部门
class ViewModelProvider(
private val store: ViewModelStore,
private val factory: Factory
) {
fun get(key: String): ViewModel {
// 1. 先查找是否已存在
var viewModel = store.get(key)
if (viewModel == null) {
// 2. 不存在则创建新的
viewModel = factory.create()
// 3. 存储起来
store.put(key, viewModel)
}
return viewModel
}
}
就像:
- 需要员工时先查看在职名单
- 如果没有才招新人
- 招来的人会登记在系统中
3. 实际工作流程
graph TD
A[Activity/Fragment] -->|持有| B[ViewModelStore]
B -->|存储| C[ViewModel]
D[配置变化] -->|保持| B
E[Activity销毁] -->|清理| B
4. 具体实现示例
// 1. Activity 中的实现
class MainActivity : ComponentActivity() {
// ViewModelStore 由 ComponentActivity 持有
private val viewModelStore = ViewModelStore()
private val viewModel by viewModels<MyViewModel> {
// 创建 ViewModel 的工厂
ViewModelProvider.Factory {
MyViewModel()
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// ViewModel 已经准备好了
}
override fun onDestroy() {
super.onDestroy()
// 只有在非配置变化导致的销毁才清理
if (!isChangingConfigurations) {
viewModelStore.clear()
}
}
}
5. 保存状态的机制
class MyViewModel : ViewModel() {
// 1. 普通数据:会在配置变化时保持
private val _data = MutableStateFlow<Data>(InitialData)
// 2. 保存状态:会在进程死亡时保存
private val _savedState = SavedStateHandle()
init {
// 在 ViewModel 创建时初始化
viewModelScope.launch {
loadData()
}
}
// 3. 清理资源
override fun onCleared() {
// ViewModel 真正销毁时调用
super.onCleared()
}
}
6. 生命周期示意
// 生命周期流程
class LifecycleExample {
fun process() {
// 1. 创建 Activity
val activity = Activity()
val viewModel = ViewModelProvider(activity).get()
// 2. 配置变化(比如旋转屏幕)
activity.recreate() // ViewModel 保持不变
// 3. 真正的销毁(比如关闭页面)
activity.finish() // ViewModel 被清理
}
}
关键点总结:
- 独立生命周期的原理:
- ViewModelStore 持有 ViewModel
- Activity/Fragment 持有 ViewModelStore
- 配置变化时 ViewModelStore 保持不变
- 只有在真正销毁时才清理
- 状态保持:
- ViewModel 中的数据在配置变化时保持
- SavedStateHandle 可以在进程死亡时保存状态
- 资源管理:
- viewModelScope 自动跟随 ViewModel 生命周期
- onCleared 在真正销毁时清理资源
这样的设计确保了:
- 数据在配置变化时的稳定性
- 资源的合理管理
- 内存的有效使用
- 更好的用户体验
绑定Activity过程
1. ViewModelStore 机制
- 每个 Activity 都会关联一个 ViewModelStore 对象
- ViewModelStore 是一个简单的 HashMap 容器,用于存储 ViewModel 实例
- key 是 ViewModel 的类名,value 是 ViewModel 实例
2. 绑定过程
// 当我们在 Activity 中调用
ViewModelProvider(this).get(MyViewModel::class.java)
实际上发生了以下步骤:
a) 获取 ViewModelStoreOwner
- Activity 实现了 ViewModelStoreOwner 接口
- 通过 ComponentActivity 中的 getViewModelStore() 方法获取 ViewModelStore
b) ViewModelProvider 创建过程
- 创建 ViewModelProvider 实例时会传入 ViewModelStoreOwner
- ViewModelProvider 会持有 ViewModelStore 的引用
- 同时会创建默认的 Factory 用于实例化 ViewModel
c) ViewModel 实例化
- 调用 get() 方法时,首先会从 ViewModelStore 中查找是否已存在对应的 ViewModel
- 如果不存在,则使用 Factory 创建新的实例并存入 ViewModelStore
3. 生命周期管理
- Activity 实现了 LifecycleOwner 接口
- 在 Activity 销毁时(非配置变更导致的销毁),会调用 ViewModelStore.clear() 清除所有 ViewModel
- 配置变更时(如旋转屏幕),Activity 会被销毁重建,但 ViewModel 会被保留
4. 源码核心实现
public class ViewModelProvider {
private final ViewModelStore store;
private final Factory factory;
public <T extends ViewModel> T get(Class<T> modelClass) {
String canonicalName = modelClass.getCanonicalName();
ViewModel viewModel = store.get(canonicalName);
if (modelClass.isInstance(viewModel)) {
return (T) viewModel;
}
viewModel = factory.create(modelClass);
store.put(canonicalName, viewModel);
return (T) viewModel;
}
}
5. 保存机制
- Activity 在配置变更时会调用 onRetainNonConfigurationInstance()
- 该方法会保存 ViewModelStore,使其在 Activity 重建时可以恢复
- 新的 Activity 实例会通过 getLastNonConfigurationInstance() 获取之前保存的 ViewModelStore
6. 关键类的职责
- ViewModelStoreOwner:定义了获取 ViewModelStore 的接口
- ViewModelStore:存储 ViewModel 实例的容器
- ViewModelProvider:创建和获取 ViewModel 的工厂类
- Factory:负责实际创建 ViewModel 实例
这种设计的优点:
- 实现了 ViewModel 的作用域管理
- 保证了配置变更时数据的存活
- 避免了内存泄漏
- 使 UI 控制器(如 Activity)和数据处理解耦
需要注意的是,这种绑定机制是通过 Jetpack 架构组件实现的,而不是 Android 框架的原生功能。这也是为什么我们需要添加相应的依赖才能使用 ViewModel。
ViewModel与Activity等的绑定流程图总结
graph TD
A[Activity/Fragment] -->|1. 调用| B["ViewModelProvider(this).get(XXViewModel.class)"]
B -->|2. 构造| C[ViewModelProvider]
C -->|3. 获取| D[ViewModelStore]
D -->|4. 查找| E{是否存在<br>ViewModel实例?}
E -->|是| F[返回已有实例]
E -->|否| G[Factory.create]
G -->|5. 创建| H[新ViewModel实例]
H -->|6. 存储| I[ViewModelStore.put]
I -->|7. 返回| J[ViewModel实例]
F -->|8. 返回| J
subgraph 配置变更保存机制
K[Activity.onRetainNonConfigurationInstance] -->|保存| L[ViewModelStore]
M[新Activity] -->|获取| N[getLastNonConfigurationInstance]
N -->|恢复| L
end
subgraph 生命周期管理
O[Activity.onDestroy] -->|非配置变更| P[ViewModelStore.clear]
O -->|配置变更| Q[保留ViewModel]
end
关键步骤说明:
-
初始化阶段
- Activity 调用 ViewModelProvider 的构造方法
- 传入 this(ViewModelStoreOwner)作为参数
-
获取 ViewModel 阶段
- ViewModelProvider.get() 方法被调用
- 通过 ViewModelStore 查找是否存在实例
- 不存在则通过 Factory 创建新实例
-
存储阶段
- 新创建的 ViewModel 被存入 ViewModelStore
- ViewModelStore 使用 HashMap 保存实例
-
配置变更处理
- Activity 销毁前保存 ViewModelStore
- 新 Activity 恢复时获取之前的 ViewModelStore
-
生命周期管理
- 监听 Activity 的生命周期
- 根据销毁原因决定是否清除 ViewModel
这个流程展示了从 ViewModel 的创建到销毁的完整生命周期,以及在配置变更时的处理机制。整个过程是由 Jetpack 架构组件自动管理的,开发者只需要关注业务逻辑的实现。
补充说明:
- ViewModelStore 是线程安全的
- ViewModel 的创建是懒加载的
- Factory 可以被自定义以支持带参数的 ViewModel 构造
- 整个机制保证了 ViewModel 的单一实例性
这个流程的设计充分体现了关注点分离和单一职责原则,使得 UI 层和数据层能够更好地解耦,同时保证了数据在配置变更时的可靠性。
ViewModel的相关流程图
让我用流程图详细展示 ViewModel 的各个关键知识点:
1. ViewModel 生命周期流程
graph TD
A[Activity/Fragment 创建] -->|创建| B[ViewModel]
B --> C{配置变化?}
C -->|是| D[ViewModel 保持]
C -->|否| E[Activity/Fragment 销毁]
E --> F[ViewModel onCleared]
D --> C
2. ViewModel 创建过程
flowchart LR
A[Activity] -->|1. 请求 ViewModel| B[ViewModelProvider]
B -->|2. 查找| C[ViewModelStore]
C -->|3.1 存在| D[返回已有 ViewModel]
C -->|3.2 不存在| E[Factory 创建新 ViewModel]
E --> F[存入 ViewModelStore]
F --> D
3. ViewModel 状态管理
graph TD
A[ViewModel 状态] --> B[普通状态]
A --> C[SavedState]
B --> D[StateFlow/LiveData]
B --> E[普通变量]
C --> F[SavedStateHandle]
D -->|配置变化| G[保持]
E -->|配置变化| G
F -->|进程死亡| H[恢复]
让我详细解释 SavedState:
1. SavedState 是什么?
class OrderViewModel(
// SavedStateHandle 用于保存和恢复数据
private val savedStateHandle: SavedStateHandle
) : ViewModel() {
// 1. 保存数据
init {
// 保存订单ID
savedStateHandle["order_id"] = "123"
// 保存订单状态
savedStateHandle["order_status"] = "PENDING"
}
// 2. 读取数据
val orderId = savedStateHandle.get<String>("order_id")
// 3. 使用 StateFlow
val orderStatus = savedStateHandle.getStateFlow("order_status", "")
}
就像是:
- 餐厅的记事本
- 即使停电(进程被杀)
- 重启后还能看到之前的记录
2. 什么时候需要用?
class ExampleViewModel(
private val savedStateHandle: SavedStateHandle
) : ViewModel() {
// ❌ 普通变量:进程死亡后数据丢失
private var normalData = "data"
// ❌ StateFlow:进程死亡后数据丢失
private val _stateFlow = MutableStateFlow("data")
// ✅ SavedStateHandle:进程死亡后可恢复
private val savedData = savedStateHandle.getStateFlow("key", "data")
}
使用场景:
- 用户填写的表单数据
- 页面滚动位置
- 关键的业务状态
3. 工作原理
// 1. 保存数据
class SaveExample(private val savedStateHandle: SavedStateHandle) : ViewModel() {
fun saveData() {
savedStateHandle["user_input"] = "用户输入" // 会自动保存到 Bundle
}
}
// 2. 进程被杀死后恢复
class RestoreExample(private val savedStateHandle: SavedStateHandle) : ViewModel() {
init {
// 自动恢复之前保存的数据
val userInput = savedStateHandle.get<String>("user_input")
// 继续使用恢复的数据
}
}
工作流程:
- 数据保存到 SavedStateHandle
- 系统将数据序列化到 Bundle
- 进程被杀死
- 用户重新打开应用
- 系统从 Bundle 恢复数据
- SavedStateHandle 重建状态
4. 使用建议
class BestPracticeViewModel(
private val savedStateHandle: SavedStateHandle
) : ViewModel() {
// 1. 定义键值
companion object {
private const val KEY_USER_DATA = "user_data"
private const val KEY_SCROLL_POSITION = "scroll_position"
}
// 2. 使用 StateFlow
val userData = savedStateHandle.getStateFlow(KEY_USER_DATA, UserData())
// 3. 更新数据
fun updateUserData(newData: UserData) {
savedStateHandle[KEY_USER_DATA] = newData
}
// 4. 保存滚动位置
fun saveScrollPosition(position: Int) {
savedStateHandle[KEY_SCROLL_POSITION] = position
}
}
优点:
- 自动保存和恢复
- 无需手动序列化
- 支持 StateFlow
- 类型安全
注意事项:
- 只能保存简单数据类型
- 不要保存大量数据
- 键值要唯一
- 注意内存使用
这样可以:
- 提升用户体验
- 保护用户数据
- 处理进程死亡
- 减少数据丢失
4. ViewModel 作用域管理
flowchart TD
A[ViewModel] -->|创建| B[viewModelScope]
B --> C[协程]
D[Activity 销毁] -->|触发| E[ViewModel.onCleared]
E -->|取消| B
E -->|清理| C
5. ViewModel 与 UI 交互
sequenceDiagram
participant UI as UI Layer
participant VM as ViewModel
participant R as Repository
UI->>VM: 1. UI Event
VM->>R: 2. 处理数据
R-->>VM: 3. 返回结果
VM-->>UI: 4. 更新 UI State
6. SavedStateHandle 工作流程
graph LR
A[ViewModel] -->|存储| B[SavedStateHandle]
B -->|保存| C[Bundle]
C -->|进程死亡| D[系统保存]
D -->|恢复| E[新 Bundle]
E -->|恢复| F[新 SavedStateHandle]
F -->|恢复| G[新 ViewModel]
7. ViewModel 清理流程
flowchart TD
A[Activity/Fragment 销毁] -->|检查| B{配置变化?}
B -->|是| C[保持 ViewModel]
B -->|否| D[ViewModelStore.clear]
D --> E[调用所有 ViewModel.onCleared]
E --> F[取消 viewModelScope]
F --> G[清理资源]
8. ViewModel 数据流
flowchart LR
A[数据源] -->|获取数据| B[Repository]
B -->|处理数据| C[ViewModel]
C -->|StateFlow/LiveData| D[UI State]
D -->|Compose/XML| E[UI 展示]
E -->|用户操作| F[UI Event]
F -->|处理事件| C
9. ViewModel 实例共享
graph TD
A[Activity] -->|共享| B[ViewModel]
C[Fragment1] -->|访问| B
D[Fragment2] -->|访问| B
B -->|状态更新| C
B -->|状态更新| D
10. 完整架构流程
graph TD
A[UI Layer] -->|Events| B[ViewModel Layer]
B -->|States| A
B -->|Data Ops| C[Repository Layer]
C -->|Results| B
subgraph "ViewModel Scope"
B -->|管理| D[ViewModelScope]
B -->|状态| E[SavedStateHandle]
end
这些流程图展示了:
- ViewModel 的生命周期管理
- 状态保存和恢复机制
- 协程作用域管理
- UI 交互流程
- 数据流向
- 资源清理过程
- 实例共享机制
帮助理解:
- ViewModel 的工作原理
- 各组件之间的关系
- 数据流转过程
- 生命周期管理
- 最佳实践方式
ViewModel中的协程的使用
1. 基本概念
想象 ViewModel 是一个工厂:
viewModelScope是工厂的工作时间- 协程是工厂里的工人
- 当工厂关门(ViewModel 清除),所有工人都会停止工作(协程取消)
2. 基础使用示例
class UserViewModel : ViewModel() {
private val _userState = MutableStateFlow<UiState>(UiState.Initial)
val userState = _userState.asStateFlow()
// 加载用户信息
fun loadUserInfo() {
// 启动一个工人(协程)去工作
viewModelScope.launch {
try {
// 1. 更新状态为加载中
_userState.value = UiState.Loading
// 2. 执行耗时操作
val user = userRepository.getUserInfo()
// 3. 更新成功状态
_userState.value = UiState.Success(user)
} catch (e: Exception) {
// 4. 更新错误状态
_userState.value = UiState.Error(e.message)
}
}
}
}
3. 不同协程作用域
graph TD
A[协程作用域] --> B[viewModelScope]
A --> C[lifecycleScope]
A --> D[GlobalScope]
B --> B1[ViewModel生命周期]
C --> C1[Activity/Fragment生命周期]
D --> D1[应用程序生命周期]
4. 常见使用场景
4.1 并行请求
class ProductViewModel : ViewModel() {
fun loadData() {
viewModelScope.launch {
// 同时派出多个工人工作
val products = async { repository.getProducts() }
val categories = async { repository.getCategories() }
// 等待所有工人完成工作
try {
val productList = products.await()
val categoryList = categories.await()
// 处理结果
} catch (e: Exception) {
// 处理错误
}
}
}
}
4.2 顺序请求
class OrderViewModel : ViewModel() {
fun processOrder() {
viewModelScope.launch {
try {
// 工人按顺序完成工作
val order = createOrder() // 第一步:创建订单
val payment = processPayment(order) // 第二步:处理支付
updateOrderStatus(payment) // 第三步:更新状态
} catch (e: Exception) {
// 处理错误
}
}
}
}
5. 错误处理
class ErrorHandlingViewModel : ViewModel() {
fun handleErrors() {
viewModelScope.launch {
try {
// 可能出错的操作
riskyOperation()
} catch (e: Exception) {
when (e) {
is NetworkException -> handleNetworkError()
is DatabaseException -> handleDatabaseError()
else -> handleUnknownError()
}
} finally {
// 清理工作
cleanup()
}
}
}
}
6. 取消操作
class SearchViewModel : ViewModel() {
private var searchJob: Job? = null
fun search(query: String) {
// 取消之前的搜索工作
searchJob?.cancel()
// 启动新的搜索
searchJob = viewModelScope.launch {
try {
delay(300) // 防抖
val results = repository.search(query)
// 处理结果
} catch (e: CancellationException) {
// 搜索被取消
}
}
}
}
7. 协程作用域选择
class MyViewModel : ViewModel() {
// 1. viewModelScope:随ViewModel生命周期
fun operation1() {
viewModelScope.launch {
// 适合:数据加载、网络请求等
}
}
// 2. 自定义作用域:特殊需求
private val customScope = CoroutineScope(
Dispatchers.IO + SupervisorJob()
)
fun operation2() {
customScope.launch {
// 适合:特殊的取消策略
}
}
}
8. 最佳实践
class BestPracticeViewModel : ViewModel() {
// 1. 状态管理
private val _state = MutableStateFlow<UiState>(UiState.Initial)
val state = _state.asStateFlow()
// 2. 错误处理
private val _error = MutableSharedFlow<String>()
val error = _error.asSharedFlow()
fun loadData() {
viewModelScope.launch {
try {
_state.value = UiState.Loading
// 使用 withContext 切换线程
val result = withContext(Dispatchers.IO) {
repository.getData()
}
_state.value = UiState.Success(result)
} catch (e: Exception) {
_state.value = UiState.Error
_error.emit(e.message ?: "Unknown error")
}
}
}
}
总结:
-
基本原则:
- 使用
viewModelScope管理协程生命周期 - 适当处理错误情况
- 注意取消不需要的操作
- 使用
-
常见用法:
- 网络请求
- 数据库操作
- 并行任务
- 顺序任务
-
注意事项:
- 正确选择协程作用域
- 合理处理异常
- 及时取消不需要的任务
- 使用适当的调度器
-
最佳实践:
- 使用 Flow 管理状态
- 统一的错误处理
- 合理的线程切换
- 清晰的代码结构
记住:
- ViewModel 是工厂(管理协程的地方)
- viewModelScope 是工作时间(协程的生命周期)
- 协程是工人(执行具体任务)
- 工厂关门(ViewModel 清除)时,所有工作自动停止(协程取消)
ViewModel为何不建议直接持有Context?
让我深入解析为什么 ViewModel 不建议直接持有 Context。
🌟 核心问题
// 错误示例
class WrongViewModel(
private val context: Context // ❌ 不应该直接持有
) : ViewModel() {
// 可能导致内存泄漏
}
// 正确示例
class CorrectViewModel(
private val repository: Repository // ✅ 依赖注入
) : ViewModel() {
// 生命周期安全
}
💡 主要原因
// 1. 内存泄漏风险
class MemoryLeakExample : ViewModel() {
/*
问题场景:
- Activity 旋转屏幕重建
- ViewModel 存活
- Context 引用无法释放
- 导致原 Activity 泄漏
*/
private val context: Context? = null // 危险!
fun riskyOperation() {
context?.let {
// 使用 Context 的操作
// 可能在 Activity 销毁后仍然持有引用
}
}
}
// 2. 生命周期不一致
class LifecycleConflictExample : ViewModel() {
/*
生命周期差异:
- Activity:配置变更时重建
- ViewModel:配置变更时保持
- Context:跟随 Activity 销毁
*/
fun demonstrateLifecycleMismatch() {
// Activity 重建
// ViewModel 保持
// Context 变得无效
}
}
⚡ 正确处理方式
// 1. 使用 Application Context
class SafeViewModel(
application: Application
) : AndroidViewModel(application) {
// 安全地使用 Application Context
private val appContext = application.applicationContext
fun safeOperation() {
// 使用 Application Context
appContext.resources
}
}
// 2. 依赖注入
class DependencyInjectionExample(
private val repository: Repository,
private val useCase: UseCase
) : ViewModel() {
// 通过依赖注入获取所需依赖
// 而不是直接持有 Context
}
// 3. 使用 Flow/LiveData
class DataStreamExample : ViewModel() {
private val _data = MutableStateFlow<Data>(Data.Empty)
val data: StateFlow<Data> = _data.asStateFlow()
// 通过数据流传递,而不是直接操作 Context
}
🔄 最佳实践
// 1. AndroidViewModel 使用
class SafeAndroidViewModel(
application: Application
) : AndroidViewModel(application) {
// 需要 Context 的操作都在 Repository 层处理
private val repository: Repository = Repository(
application.applicationContext
)
fun loadData() {
viewModelScope.launch {
repository.getData()
}
}
}
// 2. Repository 模式
class Repository(
private val context: Context
) {
// Repository 负责处理需要 Context 的操作
fun getResources(): Resources {
return context.resources
}
}
// 3. Use Case 模式
class UseCase(
private val context: Context
) {
// 业务逻辑封装在 Use Case 中
fun execute() {
// 处理需要 Context 的操作
}
}
⚠️ 常见错误
// 1. 直接持有 Activity
class ProblematicViewModel(
private val activity: Activity // ❌ 严重错误
) : ViewModel()
// 2. 保存 View 引用
class ViewHoldingViewModel : ViewModel() {
private var button: Button? = null // ❌ 不应该持有
}
// 3. 存储 Context 相关对象
class ContextDependentViewModel : ViewModel() {
private lateinit var layoutInflater: LayoutInflater // ❌ 避免
}
📊 影响分析
// 内存泄漏示例
class LeakAnalysis {
/*
泄漏场景:
1. Activity 重建
ViewModel 存活
Context 引用未释放
2. 内存占用增加
性能下降
可能导致 OOM
*/
fun demonstrateLeak() {
val viewModel = WrongViewModel(activity)
// 屏幕旋转
// Activity 重建
// 原 Activity 无法释放
}
}
💡 解决方案
- 架构层面:
- 使用依赖注入
- 采用 Repository 模式
- 实现 Clean Architecture
- 代码层面:
- 使用 AndroidViewModel
- 通过 Application Context
- 封装 Context 相关操作
- 生命周期处理:
- 遵循 MVVM 架构
- 使用 Flow/LiveData
- 正确处理配置变更
记住:
- ViewModel 不应直接持有 Context
- 使用 Application Context 或依赖注入
- 将 Context 相关操作封装在 Repository
- 注意内存泄漏风险
ViewModel 的"僵尸复活"问题
让我详细解释 ViewModel 的"僵尸复活"问题及其解决方案:
一、什么是"僵尸复活"问题?
"僵尸复活"(Zombie ViewModel)指的是:
- Activity 被系统销毁(比如配置更改或内存不足)
- ViewModel 实例仍然保留在 ViewModelStore 中
- Activity 重建时,原来的 ViewModel 会被重新关联到新的 Activity
- 如果 ViewModel 持有了原 Activity 的引用,就会导致内存泄漏或不正确的状态
二、产生原因
class MyViewModel : ViewModel() {
// 错误示例:直接持有 Activity 引用
private lateinit var activity: MainActivity
// 错误示例:持有 Context 引用
private lateinit var context: Context
// 错误示例:持有 View 引用
private lateinit var button: Button
}
主要原因:
- ViewModel 错误地持有 Activity/Context/View 等引用
- 配置更改时 Activity 重建,但 ViewModel 保持存活
- 新 Activity 实例与旧的 ViewModel 关联,导致引用错乱
三、解决方案
- 使用 AndroidViewModel
class MyAndroidViewModel(application: Application) : AndroidViewModel(application) {
// 安全地使用 Application Context
private val appContext = application.applicationContext
}
- 使用 LiveData 通信
class MyViewModel : ViewModel() {
private val _events = MutableLiveData<Event<String>>()
val events: LiveData<Event<String>> = _events
fun doSomething() {
_events.value = Event("Action completed")
}
}
- 使用 Flow 通信
class MyViewModel : ViewModel() {
private val _events = MutableSharedFlow<String>()
val events = _events.asSharedFlow()
fun doSomething() {
viewModelScope.launch {
_events.emit("Action completed")
}
}
}
- 使用 SavedStateHandle
class MyViewModel(private val savedStateHandle: SavedStateHandle) : ViewModel() {
// 安全地保存和恢复状态
var userName: String?
get() = savedStateHandle.get<String>("user_name")
set(value) = savedStateHandle.set("user_name", value)
}
- 使用 Event 包装器
class Event<T>(private val content: T) {
private var hasBeenHandled = false
fun getContentIfNotHandled(): T? {
return if (hasBeenHandled) {
null
} else {
hasBeenHandled = true
content
}
}
fun peekContent(): T = content
}
四、最佳实践
- 避免持有 Activity 引用
// 错误方式
class WrongViewModel : ViewModel() {
private var activity: Activity? = null
fun setActivity(activity: Activity) {
this.activity = activity
}
}
// 正确方式
class CorrectViewModel : ViewModel() {
private val _navigation = MutableLiveData<NavigationEvent>()
val navigation: LiveData<NavigationEvent> = _navigation
}
- 使用 Event 处理一次性事件
class MyViewModel : ViewModel() {
private val _showToast = MutableLiveData<Event<String>>()
val showToast: LiveData<Event<String>> = _showToast
fun showMessage(message: String) {
_showToast.value = Event(message)
}
}
- 使用 StateFlow 管理状态
class MyViewModel : ViewModel() {
private val _uiState = MutableStateFlow(UiState())
val uiState = _uiState.asStateFlow()
fun updateState(newState: UiState) {
_uiState.value = newState
}
}
- 正确处理配置更改
class MyViewModel : ViewModel() {
// 使用 SavedStateHandle 保存关键数据
private val savedStateHandle: SavedStateHandle
// 使用 StateFlow 管理 UI 状态
private val _uiState = MutableStateFlow<UiState>(UiState.Initial)
val uiState = _uiState.asStateFlow()
// 使用 SharedFlow 处理一次性事件
private val _events = MutableSharedFlow<UiEvent>()
val events = _events.asSharedFlow()
}
五、注意事项
-
永远不要在 ViewModel 中持有:
- Activity/Fragment 引用
- View 引用
- Context 引用(除了 Application Context)
-
使用正确的通信方式:
- LiveData/StateFlow 用于状态
- SharedFlow/Event 用于一次性事件
- SavedStateHandle 用于状态保存
-
正确处理生命周期:
- 在 onCleared() 中清理资源
- 使用 viewModelScope 管理协程
- 注意内存泄漏问题
通过以上方案,可以有效避免 ViewModel 的"僵尸复活"问题,保持应用状态的一致性和可靠性。
为什么Fragment中获取的ViewModel会和Activity共享?如何实现作用域隔离?
让我来解释 Fragment 和 Activity 中 ViewModel 共享的原理以及如何实现作用域隔离:
1. 为什么会共享?
// Fragment 中
class MyFragment : Fragment() {
// 默认情况下,这里获取的 ViewModel 实际上是绑定到 Activity 的生命周期
val viewModel: MyViewModel by viewModels()
}
// Activity 中
class MyActivity : AppCompatActivity() {
// 这里获取的 ViewModel 和上面的 Fragment 是同一个实例
val viewModel: MyViewModel by viewModels()
}
原因是:
by viewModels()默认使用 Activity 作为 ViewModelStoreOwner- ViewModelStore 是存储在 ViewModelStoreOwner 中的
- 同一个 ViewModelStoreOwner 中的同类型 ViewModel 只会创建一个实例
2. 如何实现作用域隔离:
方法一:使用 Fragment 自己作为 ViewModelStoreOwner
class MyFragment : Fragment() {
// 指定 this 作为 ViewModelStoreOwner,确保 ViewModel 仅在 Fragment 范围内
private val viewModel: MyViewModel by viewModels { this }
// 或者直接使用
private val viewModel: MyViewModel by viewModels() // Fragment 默认使用自己作为 owner
}
方法二:使用不同的 ViewModel 类
// Fragment 专用的 ViewModel
class FragmentViewModel : ViewModel()
// Activity 专用的 ViewModel
class ActivityViewModel : ViewModel()
class MyFragment : Fragment() {
// 这个 ViewModel 不会和 Activity 共享
private val viewModel: FragmentViewModel by viewModels()
}
class MyActivity : AppCompatActivity() {
// 这个是不同的 ViewModel 类型
private val viewModel: ActivityViewModel by viewModels()
}
3. 常见使用场景:
场景一:Fragment 需要自己的独立 ViewModel
class UserListFragment : Fragment() {
// Fragment 范围的 ViewModel
private val viewModel: UserListViewModel by viewModels()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// 这个 ViewModel 的生命周期跟随 Fragment
viewModel.users.observe(viewLifecycleOwner) { users ->
// 更新 UI
}
}
}
场景二:多个 Fragment 需要共享数据
class SharedViewModel : ViewModel() {
private val _sharedData = MutableLiveData<String>()
val sharedData: LiveData<String> = _sharedData
}
class Fragment1 : Fragment() {
// 使用 Activity 范围的 ViewModel
private val sharedViewModel: SharedViewModel by activityViewModels()
}
class Fragment2 : Fragment() {
// 同样使用 Activity 范围的 ViewModel
private val sharedViewModel: SharedViewModel by activityViewModels()
}
场景三:嵌套 Fragment 的情况
class ParentFragment : Fragment() {
// 父 Fragment 的 ViewModel
private val parentViewModel: ParentViewModel by viewModels()
// 如果要和子 Fragment 共享,使用 Fragment 作为 ViewModelStoreOwner
private val sharedViewModel: SharedViewModel by viewModels()
}
class ChildFragment : Fragment() {
// 获取父 Fragment 的 ViewModel
private val sharedViewModel: SharedViewModel by viewModels(
ownerProducer = { requireParentFragment() }
)
}
4. 注意事项:
1. 作用域选择建议:
// 当数据只在单个 Fragment 使用时
private val viewModel: MyViewModel by viewModels()
// 当数据需要在多个 Fragment 间共享时
private val viewModel: SharedViewModel by activityViewModels()
// 当需要在父子 Fragment 间共享时
private val viewModel: SharedViewModel by viewModels { requireParentFragment() }
2. 生命周期注意:
class MyFragment : Fragment() {
private val viewModel: MyViewModel by viewModels()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// 使用 viewLifecycleOwner 而不是 this 来观察数据
viewModel.data.observe(viewLifecycleOwner) { data ->
// 更新 UI
}
}
}
3. 内存管理:
- Fragment 范围的 ViewModel 会在 Fragment 销毁时清理
- Activity 范围的 ViewModel 会在 Activity 销毁时清理
- 选择合适的作用域可以避免内存泄漏
总结:
- 默认情况下 Fragment 中的 ViewModel 是和 Activity 共享的
- 使用
by viewModels()可以让 ViewModel 仅在 Fragment 范围内 - 使用不同的 ViewModel 类也可以实现隔离
- 根据实际需求选择合适的作用域很重要
多个Activity共享ViewModel
在 Android 中让两个 Activity 共享 ViewModel 主要有两种方法:
1. 使用 ActivityViewModel
// 1. 首先定义共享的 ViewModel
class SharedViewModel : ViewModel() {
private val _sharedData = MutableLiveData<String>()
val sharedData: LiveData<String> = _sharedData
fun updateData(newData: String) {
_sharedData.value = newData
}
}
// 2. 在两个 Activity 中使用同一个 ViewModelStore Owner
class MainActivity : AppCompatActivity() {
private val sharedViewModel: SharedViewModel by activityViewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 观察数据变化
sharedViewModel.sharedData.observe(this) { data ->
// 处理数据更新
}
}
}
class SecondActivity : AppCompatActivity() {
private val sharedViewModel: SharedViewModel by activityViewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 更新数据
sharedViewModel.updateData("新数据")
}
}
2. 使用自定义 ViewModelStoreOwner
// 1. 创建一个自定义的 ViewModelStoreOwner
class MyApplication : Application(), ViewModelStoreOwner {
private val appViewModelStore: ViewModelStore by lazy {
ViewModelStore()
}
override fun getViewModelStore(): ViewModelStore = appViewModelStore
}
// 2. 在 Activity 中使用
class MainActivity : AppCompatActivity() {
private val sharedViewModel: SharedViewModel by viewModels {
ViewModelProvider.AndroidViewModelFactory.getInstance((application as MyApplication))
}
}
class SecondActivity : AppCompatActivity() {
private val sharedViewModel: SharedViewModel by viewModels {
ViewModelProvider.AndroidViewModelFactory.getInstance((application as MyApplication))
}
}
3. 使用 Navigation Component(推荐)
如果使用 Navigation Component,可以这样:
// 在 nav_graph.xml 中定义导航
<navigation>
<fragment
android:id="@+id/firstFragment"
android:name=".FirstFragment">
<action
android:id="@+id/action_first_to_second"
app:destination="@id/secondFragment" />
</fragment>
<fragment
android:id="@+id/secondFragment"
android:name=".SecondFragment" />
</navigation>
// 在 Fragment 中共享 ViewModel
class FirstFragment : Fragment() {
private val sharedViewModel: SharedViewModel by activityViewModels()
}
class SecondFragment : Fragment() {
private val sharedViewModel: SharedViewModel by activityViewModels()
}
4. 使用 Hilt(依赖注入方式)
@HiltAndroidApp
class MyApplication : Application()
@AndroidEntryPoint
class MainActivity : AppCompatActivity()
@AndroidEntryPoint
class SecondActivity : AppCompatActivity()
@HiltViewModel
class SharedViewModel @Inject constructor() : ViewModel()
// 在两个 Activity 中注入
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
@Inject
lateinit var sharedViewModel: SharedViewModel
}
@AndroidEntryPoint
class SecondActivity : AppCompatActivity() {
@Inject
lateinit var sharedViewModel: SharedViewModel
}
最佳实践建议
- 使用 Navigation Component
- 如果你的 App 使用单 Activity 多 Fragment 架构
- 这是 Google 推荐的现代架构方式
- 使用 Hilt
- 如果你的项目使用依赖注入
- 提供更好的解耦和测试性能
- 使用 Application 范围
- 如果确实需要在多个 Activity 间共享数据
- 注意内存泄漏问题
// 示例:完整的共享 ViewModel 实现
class SharedViewModel : ViewModel() {
// 使用 Flow 而不是 LiveData 以获得更好的协程支持
private val _sharedState = MutableStateFlow<UiState>(UiState.Initial)
val sharedState = _sharedState.asStateFlow()
sealed class UiState {
object Initial : UiState()
data class Content(val data: String) : UiState()
data class Error(val message: String) : UiState()
}
fun updateState(newData: String) {
viewModelScope.launch {
try {
_sharedState.value = UiState.Content(newData)
} catch (e: Exception) {
_sharedState.value = UiState.Error(e.message ?: "Unknown error")
}
}
}
// 清理资源
override fun onCleared() {
super.onCleared()
// 清理工作
}
}
记住:
- ViewModel 生命周期长于 Activity
- 注意内存泄漏
- 合理管理数据状态
- 考虑使用 SavedStateHandle 保存状态