一、ViewModel 的本质与价值
在Android开发中,屏幕旋转是一个常见的配置更改场景。当用户旋转设备时,Activity会被销毁并重新创建,导致临时数据丢失。传统解决方案(如onSaveInstanceState
)只适合保存少量简单数据。ViewModel应运而生,它解决了配置更改导致的数据丢失问题,同时将数据逻辑与UI分离,成为现代Android架构的核心组件。 主要解决两大问题:
-
数据持久化问题:
- 屏幕旋转时保留数据
- Activity重建时避免重复加载
-
生命周期安全问题:
- 自动取消异步任务
- 防止内存泄漏
一、ViewModel 使用指南:三步构建数据保险箱
1. 创建ViewModel子类
kotlin
class UserViewModel : ViewModel() {
private val _userData = MutableLiveData<User>()
val userData: LiveData<User> = _userData
fun loadUser(userId: String) {
viewModelScope.launch {
_userData.value = repository.getUser(userId)
}
}
override fun onCleared() {
// 清理资源
super.onCleared()
}
}
2. 在Activity/Fragment中获取实例
kotlin
class UserActivity : AppCompatActivity() {
// 使用委托简化获取
private val viewModel: UserViewModel by viewModels {
UserViewModelFactory(provideUserRepository())
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_user)
// 观察数据变化
viewModel.userData.observe(this) { user ->
updateUI(user)
}
loadButton.setOnClickListener {
viewModel.loadUser("123")
}
}
}
3. 自定义Factory实现依赖注入
kotlin
class UserViewModelFactory(
private val repository: UserRepository
) : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return UserViewModel(repository) as T
}
}
二、ViewModel 工作原理:数据存活的魔法
核心三剑客
- ViewModelStoreOwner:Activity/Fragment作为"房东"
- ViewModelStore:房东持有的"保险箱仓库"(内部为Map结构)
- ViewModel:存放在仓库中的"数据保险箱"
配置更改时的数据保留流程

三、源码亮点解析:精妙设计揭秘
1. ViewModelStore的保存与恢复机制
核心代码:
ini
// ComponentActivity.java
public ViewModelStore getViewModelStore() {
if (mViewModelStore == null) {
// 尝试从上次配置更改恢复
NonConfigurationInstances nc = getLastNonConfigurationInstance();
if (nc != null) mViewModelStore = nc.viewModelStore;
if (mViewModelStore == null) mViewModelStore = new ViewModelStore();
}
return mViewModelStore;
}
public Object onRetainNonConfigurationInstance() {
NonConfigurationInstances nci = new NonConfigurationInstances();
nci.viewModelStore = mViewModelStore; // 保存仓库
return nci;
}
设计精妙之处:
- 利用Activity的
onRetainNonConfigurationInstance
生命周期钩子 - 通过
NonConfigurationInstances
实现轻量级状态保存 - 避免序列化开销,直接传递对象引用
2. Factory模式:解耦的艺术
类关系图:
优势:
- 完全解耦ViewModel的构造逻辑
- 支持依赖注入(构造函数参数传递)
- 为Hilt等DI框架提供标准接入点
3. 资源清理:mBagOfTags的妙用
自动资源管理流程:
scss
// ViewModel.java
void clear() {
for (Object value : mBagOfTags.values()) {
if (value instanceof Closeable) {
((Closeable) value).close(); // 自动关闭资源
}
}
onCleared(); // 开发者清理点
}
应用场景:
- 自动管理协程作用域(viewModelScope)
- 统一关闭数据库连接、网络请求等资源
- 避免手动资源释放的遗漏
4. 线程安全:双重校验锁实现
单例保障逻辑:
ini
ViewModel viewModel = store.get(key);
if (viewModel == null) {
viewModel = factory.create(modelClass);
ViewModel existing = store.putIfAbsent(key, viewModel);
if (existing != null) {
viewModel = existing; // 使用已存在实例
viewModel.onCleared(); // 清理新创建的冗余实例
}
}
return viewModel;
关键点:
putIfAbsent
保证原子操作- 防止多线程环境下的重复创建
- 确保ViewModel实例全局唯一
5. 协程集成:viewModelScope的魔法
实现原理:
kotlin
val ViewModel.viewModelScope: CoroutineScope
get() {
val scope: CoroutineScope? = this.getTag(JOB_KEY)
if (scope != null) return scope
return setTagIfAbsent(JOB_KEY,
CloseableCoroutineScope(SupervisorJob()))
}
internal class CloseableCoroutineScope(...) : Closeable {
override fun close() { coroutineContext.cancel() } // 自动取消
}
价值:
- 协程生命周期与ViewModel绑定
- ViewModel销毁时自动取消所有子协程
- 避免异步操作导致的内存泄漏
四、ViewModel 最佳实践
-
职责分离原则:
- ViewModel:数据处理和业务逻辑
- Activity/Fragment:UI展示和用户交互
- 永远不要在ViewModel中持有View引用
-
资源管理:
kotlinoverride fun onCleared() { super.onCleared() networkRequest?.cancel() database.close() }
-
架构配合:
- 结合LiveData实现数据观察
- 使用Repository模式管理数据源
- 通过DataBinding实现数据绑定