ViewModel 在什么时候被销毁

在 Android 中,ViewModel 的销毁时机取决于它的作用域(即它关联的 ActivityFragment 的生命周期)。以下是具体的销毁规则:


1. ViewModel 被销毁的触发条件 • 当关联的 ActivityFragment 被永久销毁时(例如:用户按下返回键退出、调用 finish() 关闭 Activity,或系统因资源不足强制回收)。

• 当 Fragmentremovereplace 并从回退栈(Back Stack)中彻底移除时(如果 Fragment 未加入回退栈,则在 replace 时会立即销毁)。


2. ViewModel 不会被销毁的情况 • 配置变化(如屏幕旋转、语言切换):

ViewModel 的设计核心是 "在配置变化时存活"。当 Activity/Fragment 因配置变化重建时,ViewModel 会被保留,避免数据丢失。


3. ViewModel 的销毁流程

  1. 系统调用 onCleared() 方法:

    当 ViewModel 即将被销毁时,会自动触发 onCleared(),开发者可以在这里释放资源(如取消协程、关闭数据库连接等)。

    kotlin 复制代码
    class MyViewModel : ViewModel() {
        private val job = Job()
        override fun onCleared() {
            job.cancel() // 释放资源
            super.onCleared()
        }
    }
  2. 关联的 viewModelScope 自动取消:

    如果使用 viewModelScope 启动协程,当 ViewModel 销毁时,所有未完成的协程会自动取消(避免内存泄漏)。


4. 示例场景

场景 ViewModel 是否销毁?
旋转屏幕 ❌ 不销毁(配置变化)
从 Activity A 跳转到 B ❌ 不销毁(A 进入后台,未被销毁)
关闭 Activity(按返回键) ✅ 销毁(Activity 永久结束)
Fragment 被 replace 移除 ✅ 销毁(如果未加入回退栈)

ViewModel 的销毁与 Activity/Fragment 的 永久销毁 紧密绑定,而 不会因配置变化或临时进入后台 被销毁。这种设计保证了数据在屏幕旋转等场景下的稳定性,同时避免了内存泄漏。

在 Android Jetpack 的源码中,onCleared() 的调用时机与 Activity/Fragment 的生命周期销毁逻辑紧密相关。以下是其触发机制和源码实现的详细分析:


1. 触发时机 onCleared() 会在以下场景被调用: • 当 ActivityFragment 被永久销毁(非配置变化导致的重建,例如用户退出或调用 finish())。

• 当 Fragment 被彻底移除(未加入回退栈,或被 replace 后销毁)。


2. 源码实现流程 onCleared() 的调用逻辑通过 ViewModelStoreComponentActivity/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 清理由 FragmentManagerFragmentManagerViewModel 控制:

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 在正确时机释放资源。

相关推荐
阿巴斯甜11 小时前
Android 报错:Zip file '/Users/lyy/develop/repoAndroidLapp/l-app-android-ble/app/bu
android
Kapaseker11 小时前
实战 Compose 中的 IntrinsicSize
android·kotlin
xq952712 小时前
Andorid Google 登录接入文档
android
黄林晴13 小时前
告别 Modifier 地狱,Compose 样式系统要变天了
android·android jetpack
冬奇Lab1 天前
Android触摸事件分发、手势识别与输入优化实战
android·源码阅读
城东米粉儿1 天前
Android MediaPlayer 笔记
android
Jony_1 天前
Android 启动优化方案
android
阿巴斯甜1 天前
Android studio 报错:Cause: error=86, Bad CPU type in executable
android
张小潇1 天前
AOSP15 Input专题InputReader源码分析
android
_小马快跑_1 天前
Kotlin | 协程调度器选择:何时用CoroutineScope配置,何时用launch指定?
android