问题一:ViewModel 持有构造它的 Activity/Fragment,会导致内存泄漏吗?
答案:是的,一定会导致内存泄漏(除非你手动清空,但那很难维护)。为什么?
这依然是 生命周期不对等 造成的。
- 引用的本质:当你把
Activity的this传给ViewModel时,ViewModel内部就有了一个指向该Activity内存地址的指针(强引用)。 - 存活时间差异:
Activity:是"短命"的。屏幕旋转、系统语言切换、分屏模式、或者系统内存不足时,它都会被销毁(onDestroy)。ViewModel:是"长命"的。它的设计目的就是为了在Activity重建期间存活下来。只有当Activity彻底退出(按返回键退出或调用finish())时,ViewModel才会清除。
- 场景推演(旋转屏幕):
Activity A (实例1)创建了ViewModel,并将this(实例1) 传给它。用户旋转屏幕。系统销毁Activity A (实例1)。 - 关键点来了:系统本该回收
(GC) 实例1的内存。但是,ViewModel还活着,并且手里死死抓着实例1的引用。
结果:GC 无法回收 实例1。系统创建了Activity A (实例2),并复用了同一个ViewModel。此时,内存里有:正在显示的 实例2 + 无法回收的僵尸 实例1(包含它的 View、图片等)。这就构成了标准的内存泄漏。
源码分析
核心逻辑:ViewModel 被存储在 ViewModelStore 中,而 ViewModelStore 的生命周期 长于 Activity 的配置重建(如旋转)。
Activity 销毁时: 系统调用 onRetainNonConfigurationInstance()。这里会把 mViewModelStore 保存起来,不销毁。
java
// ComponentActivity.java
// 1. Activity 销毁时:系统特意把 ViewModelStore 存起来
public final Object onRetainNonConfigurationInstance() {
// ...
NonConfigurationInstances nci = new NonConfigurationInstances();
// 【核心罪魁祸首】:把包含 ViewModel 的 Store 保存到 nci 对象中
nci.viewModelStore = mViewModelStore;
return nci; // 返回给系统进程保存,不随 Activity 销毁
}
Activity 重建时: 在 onCreate 中,系统把之前保存的 ViewModelStore 又还给了新 Activity。
kotlin
// ComponentActivity.java
// 2. 新 Activity 重建时:把 ViewModelStore 取回来
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
NonConfigurationInstances nc = (NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
// 【恢复】:新 Activity 拿到了旧的 ViewModelStore
// 此时 ViewModel 依然活着,它手里还抓着 "旧 Activity" 的引用 -> 泄漏
mViewModelStore = nc.viewModelStore;
}
}
结论:
- 流程:
Activity A (销毁)->ViewModelStore (存活)->Activity A (重建)。 - 泄漏点:如果
ViewModel内部持有了Activity A的引用(this),因为ViewModel在ViewModelStore里没死,所以Activity A也死不了 ->内存泄漏。