在 Android 中,ViewModel 的销毁时机取决于它的作用域(即它关联的 Activity
或 Fragment
的生命周期)。以下是具体的销毁规则:
1. ViewModel 被销毁的触发条件 • 当关联的 Activity
或 Fragment
被永久销毁时(例如:用户按下返回键退出、调用 finish()
关闭 Activity
,或系统因资源不足强制回收)。
• 当 Fragment
被 remove
或 replace
并从回退栈(Back Stack)中彻底移除时(如果 Fragment
未加入回退栈,则在 replace
时会立即销毁)。
2. ViewModel 不会被销毁的情况 • 配置变化(如屏幕旋转、语言切换):
ViewModel 的设计核心是 "在配置变化时存活"。当 Activity/Fragment
因配置变化重建时,ViewModel 会被保留,避免数据丢失。
3. ViewModel 的销毁流程
-
系统调用
onCleared()
方法:当 ViewModel 即将被销毁时,会自动触发
onCleared()
,开发者可以在这里释放资源(如取消协程、关闭数据库连接等)。kotlinclass MyViewModel : ViewModel() { private val job = Job() override fun onCleared() { job.cancel() // 释放资源 super.onCleared() } }
-
关联的
viewModelScope
自动取消:如果使用
viewModelScope
启动协程,当 ViewModel 销毁时,所有未完成的协程会自动取消(避免内存泄漏)。
4. 示例场景
场景 | ViewModel 是否销毁? |
---|---|
旋转屏幕 | ❌ 不销毁(配置变化) |
从 Activity A 跳转到 B | ❌ 不销毁(A 进入后台,未被销毁) |
关闭 Activity(按返回键) | ✅ 销毁(Activity 永久结束) |
Fragment 被 replace 移除 | ✅ 销毁(如果未加入回退栈) |
ViewModel 的销毁与 Activity/Fragment
的 永久销毁 紧密绑定,而 不会因配置变化或临时进入后台 被销毁。这种设计保证了数据在屏幕旋转等场景下的稳定性,同时避免了内存泄漏。
在 Android Jetpack 的源码中,onCleared()
的调用时机与 Activity
/Fragment
的生命周期销毁逻辑紧密相关。以下是其触发机制和源码实现的详细分析:
1. 触发时机 onCleared()
会在以下场景被调用: • 当 Activity
或 Fragment
被永久销毁(非配置变化导致的重建,例如用户退出或调用 finish()
)。
• 当 Fragment
被彻底移除(未加入回退栈,或被 replace
后销毁)。
2. 源码实现流程 onCleared()
的调用逻辑通过 ViewModelStore
和 ComponentActivity
/Fragment
的生命周期监听实现,以下是核心源码链路:
(1) ViewModelStore 的清理逻辑 所有 ViewModel 实例存储在 ViewModelStore
中,当需要销毁时,会调用 ViewModelStore.clear()
:
java
// 源码:ViewModelStore.java
public final class ViewModelStore {
private final HashMap<String, ViewModel> mMap = new HashMap<>();
public final void clear() {
for (ViewModel vm : mMap.values()) {
vm.clear(); // 触发 onCleared()
}
mMap.clear();
}
}
(2) Activity 中的触发链路 • ComponentActivity
监听生命周期:
ComponentActivity
在构造函数中注册了一个 LifecycleEventObserver
,监听 ON_DESTROY
事件:
java
// 源码:ComponentActivity.java
public ComponentActivity() {
getLifecycle().addObserver(new LifecycleEventObserver() {
@Override
public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
if (event == Lifecycle.Event.ON_DESTROY) {
// 判断是否因配置变化导致销毁
if (!isChangingConfigurations()) {
getViewModelStore().clear(); // 触发 ViewModelStore.clear()
}
}
}
});
}
• 关键条件:!isChangingConfigurations()
确保仅在永久销毁时清理 ViewModel。
(3) Fragment 中的触发链路 • FragmentManagerViewModel
管理销毁:
Fragment 的 ViewModel 清理由 FragmentManager
和 FragmentManagerViewModel
控制:
java
// 源码:FragmentManagerViewModel.java
void clearNonConfigState(@NonNull Fragment f) {
if (f.mViewModelStore != null) {
f.mViewModelStore.clear(); // 触发 ViewModelStore.clear()
}
}
• 当 Fragment
被永久移除时,FragmentManager
会调用此方法清理关联的 ViewModel。
3. 总结:源码调用链
scss
Activity/Fragment 销毁(非配置变化)
→ LifecycleObserver 触发 ON_DESTROY 事件
→ 调用 ViewModelStore.clear()
→ 遍历所有 ViewModel 实例并调用 onCleared()
→ 开发者重写的 onCleared() 执行资源释放
4. 开发者如何利用 onCleared() 在自定义 ViewModel 中重写 onCleared()
,释放资源或取消异步任务:
kotlin
class MyViewModel : ViewModel() {
private val job = Job()
init {
// 启动协程
viewModelScope.launch(job) { /* ... */ }
}
override fun onCleared() {
super.onCleared()
job.cancel() // 手动取消协程(viewModelScope 会自动取消,此处仅为示例)
}
}
关键点 • onCleared()
由系统自动调用,开发者无需手动触发。
• 配置变化不会触发 onCleared()
,因为 ViewModel 会被保留。
• 源码通过 ViewModelStore
和生命周期监听 的协作,确保 ViewModel 在正确时机释放资源。