目录
  1. 1. 一、ViewModel 的核心能力:跨配置重建
  2. 2. 二、源码解析:ViewModel 是如何”存活”下来的
  3. 3. 三、ViewModelProvider 与 Factory
  4. 4. 四、ViewModelScope:协程的最佳拍档
  5. 5. 面试常考问题
JetPack全家桶(三)之ViewModel界面控制器

一、ViewModel 的核心能力:跨配置重建

Android 的 Activity/Fragment 在屏幕旋转时会经历完整的销毁和重建流程,这会导致普通成员变量中的数据丢失。传统的解决方案是用 onSaveInstanceState() 将数据序列化保存再恢复,但这对大体积对象(如网络数据列表、Bitmap)效率极低且无法保存非序列化对象。ViewModel 的出现彻底解决了这个问题——ViewModel 存储于 ViewModelStore 中,而 ViewModelStore 的生命周期与 Activity 的配置变更无关。

二、源码解析:ViewModel 是如何”存活”下来的

核心在于 ViewModelStoreOwnerViewModelStore。以 Activity 为例(AOSP 路径:/lifecycle/viewmodel/src/main/java/androidx/lifecycle/ViewModelProvider.kt),ComponentActivity 实现了 ViewModelStoreOwner 接口。Activity 首次创建时会 new ViewModelStore(),在配置变更重建时,系统通过 NonConfigurationInstances 机制将旧的 ViewModelStore 传递给新的 Activity 实例。

关键代码路径:

// ComponentActivity.java (androidx.activity)
getLastNonConfigurationInstance() -> NonConfigurationInstances.viewModelStore

当 Activity 因屏幕旋转而销毁时,系统在 retainNonConfigurationInstances() 中将 ViewModelStore 保存;新建 Activity 时通过 getLastNonConfigurationInstance() 恢复。ViewModelStore 内部是一个 HashMap<String, ViewModel>,key 来自 ViewModel 类名,确保同名 ViewModel 获取的是同一个实例。

三、ViewModelProvider 与 Factory

ViewModelProvider 是获取 ViewModel 的门面类,通过 ViewModelStoreOwner 拿到 ViewModelStore。默认的 Factory 通过反射调用 ViewModel 的无参构造。如果 ViewModel 需要构造参数(如注入 Repository),需要使用自定义 Factory:

class MyViewModel(private val repo: Repository) : ViewModel()

class MyFactory(private val repo: Repository) : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return MyViewModel(repo) as T
}
}

四、ViewModelScope:协程的最佳拍档

ViewModel 实现的另一个重要扩展是 viewModelScope(来自 lifecycle-viewmodel-ktx)。它是一个绑定到 ViewModel 的 CoroutineScope,在 ViewModel 的 onCleared() 方法被调用时自动取消。这意味着启动在 viewModelScope 中的所有协程无需手动管理生命周期,彻底解决了”协程泄露导致内存泄漏”的问题。

class UserViewModel : ViewModel() {
private val _users = MutableLiveData<List<User>>()
val users: LiveData<List<User>> = _users

init {
viewModelScope.launch(Dispatchers.IO) {
_users.postValue(userRepository.getUsers())
}
}
}

面试常考问题

Q1: ViewModel 与 onSaveInstanceState 如何选择?
A: ViewModel 仅能抵御 Activity 的配置变更(如屏幕旋转)导致的重建,数据保存在内存中。当进程被系统杀死(低内存回收、用户主动退出)时,ViewModel 数据会丢失。onSaveInstanceState 将数据序列化写入 Bundle,可以跨进程恢复。因此:大对象和业务数据用 ViewModel(内存中,无需序列化),小量关键状态用 onSaveInstanceState(如列表滚动位置、输入框文本)。

Q2: Fragment 之间如何共享 ViewModel?
A: 使用 Activity 级别的 ViewModelStoreOwner。在 Fragment 中通过 ViewModelProvider(requireActivity()) 获取 ViewModel,两个 Fragment 拿到的将是同一个 ViewModel 实例。这种方式比传统接口回调或 EventBus 更简洁且无损(不依赖序列化/AIDL),是 Google 推荐的 Fragment 间通信方案。

Q3: ViewModel 能持有 Context 的引用吗?
A: 不能持有 Activity/Fragment 的 Context。AndroidViewModel 是 ViewModel 的子类,其构造函数接受 Application Context 引用,这个 Context 生命周期与应用进程相同,不存在泄漏风险。如果 ViewModel 需要 Context 做临时操作(如查询数据库路径),应该使用依赖注入传入 Application Context,而非 Activity 的 Context。

打赏
  • 微信
  • 支付宝

评论