解析Android ViewModel:生命周期管理、配置变更与Fragment间数据共享机制

目录

为何使用ViewModel:

ViewModel 在 Fragment 中不会因配置改变而销毁的原理

  ViewModel 能在 Fragment 中共享的原理

为何使用ViewModel:

1、配合MVVM模式可以提供和管理UI界面数据,将用户交互全都放在ViewModel中,Activity or Fragment只负责显示界面

2、可以感知Activity的生命周期

3、配合LiveData共享数据

4、能够防止内存泄漏

5、等等

ViewModel 概览  |  App architecture  |  Android Developers

ViewModel生命周期:

ViewModel 的生命周期与它所属的 Activity 或 Fragment 的生命周期紧密相关,当 Activity/Fragment 首次创建时(如 onCreate() 方法中),通过 ViewModelProvider 创建 ViewModel 实例。

存活条件:ViewModel 会持续存活,直到其关联的 Activity/Fragment 完全销毁(即非配置变更导致的销毁)。

  • 关键场景

    • 配置变更(如旋转屏幕):Activity/Fragment 销毁重建,ViewModel 不销毁,数据得以保留。

    • 正常退出(如用户按返回键):Activity/Fragment 永久销毁,ViewModel 随之销毁。

    • 被系统回收:若 Activity/Fragment 因系统资源不足被回收,ViewModel 也会被销毁。

ViewModel 的销毁

  • 触发时机:当关联的 Activity/Fragment 永久销毁时(如调用 finish() 或用户导航离开),ViewModel 会调用 onCleared() 方法。

  • onCleared() 的作用

    • 释放资源(如取消异步任务、关闭数据库连接)。

    • 避免内存泄漏(例如清除对 Context 的引用)。

如何使用:

创建ViewModel:

class MyViewModel : ViewModel() {

    // 使用 MutableLiveData 存储数据
    private val _counter = MutableLiveData<Int>(0)
    
    // 对外暴露不可变的 LiveData
    val counter: LiveData<Int> get() = _counter

    // 业务逻辑方法
    fun incrementCounter() {
        _counter.value = (_counter.value ?: 0) + 1
    }

    fun resetCounter() {
        _counter.value = 0
    }

    // ViewModel 销毁时的清理工作
    override fun onCleared() {
        super.onCleared()
        // 释放资源...
    }
}

带参数初始化:
默认情况下, ViewModel 通过无参构造函数创建实例对象,当然如果你需要在 ViewModel 中使用其他参数,你也可以传递自定义的 Factory。

// 使用 ViewModelProvider.Factory
class MyViewModelFactory(private val initialValue: Int) : ViewModelProvider.Factory {
    override fun <T : ViewModel> create(modelClass: Class<T>): T {
        return MyViewModel(initialValue) as T
    }
}

// 在 Activity 中使用
val factory = MyViewModelFactory(100)
viewModel = ViewModelProvider(this, factory).get(MyViewModel::class.java)

在Activity中的onCreate中使用:

MyViewModel model = ViewModelProviders.of(this).get(MyViewModel.class);

在Kotlin中使用activityViewModels()创建ViewModel:

在Fragment中我们可以使用activityViewModels或者viewModels来创建。
class HomeFragment:Fragment() {
    val activityViewModel:MainViewModel by activityViewModels()
    //或者
    val viewModel:MainViewModel by viewModels()
}

ViewModel 在 Fragment 中不会因配置改变而销毁的原理

在 Activity 中提供了 onRetainNonConfigurationInstance 方法,用于处理配置发生改变时数据的保存。随后在重新创建的 Activity 中调用 getLastNonConfigurationInstance 获取上次保存的数据。我们不能直接重写上述方法,如果想在 Activity 中自定义想要恢复的数据,需要调用上述两个方法的内部方法:

  • onRetainCustomNonConfigurationInstance()

  • getLastCustomNonConfigurationInstance()

    public class MainActivity extends AppCompatActivity {
    
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            String name = (String) getLastCustomNonConfigurationInstance();
            if (!TextUtils.isEmpty(name)) {
                //获取恢复后的数据,执行相应操作
            }
        }
    
    //你可以可以在onStart中,获取恢复的数据
    //    @Override
    //    protected void onStart() {
    //        super.onStart();
    //        String name = (String) getLastCustomNonConfigurationInstance();
    //        if (!TextUtils.isEmpty(name)) {
    //        }
    //    }
    
        @Nullable
        @Override
        public Object onRetainCustomNonConfigurationInstance() {
            return "AndyJennifer";
        }  
    }
    

    在官方代码中重写了 onRetainNonConfigurationInstance 方法,在该方法中保存了 ViewModelStore (ViweModelStore 中存储了 ViewModel ),进而也保存了 ViewModel,具体代码如下所示:

        public final Object onRetainNonConfigurationInstance() {
            Object custom = onRetainCustomNonConfigurationInstance();
    
            ViewModelStore viewModelStore = mViewModelStore;
            if (viewModelStore == null) {
                NonConfigurationInstances nc =
                        (NonConfigurationInstances) getLastNonConfigurationInstance();
                if (nc != null) {
                    viewModelStore = nc.viewModelStore;
                }
            }
    
            if (viewModelStore == null && custom == null) {
                return null;
            }
    
            //将ViewModel存储在 NonConfigurationInstances 对象中
            NonConfigurationInstances nci = new NonConfigurationInstances();
            nci.custom = custom;
            nci.viewModelStore = viewModelStore;
            return nci;
        }
    

    ViewModel 在 Fragment 中不会因配置改变而销毁的原因其实是因为其声明的 ViewModel 是存储在 FragmentManagerViewModel 中的,而 FragmentManagerViewModel 是存储在宿主 Activity 中的 ViewModelStore 中,又因 Activity 中 ViewModelStore不会因配置改变而销毁,故 Fragment 中 ViewModel 也不会因配置改变而销毁。

      ViewModel 能在 Fragment 中共享的原理

    如果我们想从 Fragment A 获取 Fragment B 中的数据,那么我们只有在 Activity 中的 ViewModelStore 下添加 ViewModel。这样Fragment A和Fragment B能够共享一个Activity,只有这样,我们才能在不同 Fragment 中获取相同的数据。这也是为什么在 Fragment 中使用共享的 ViewModel 时,我们要在调用ViewModelProvider.of() 创建 ViewModel 时需要传入 getActivity() 的原因。

    评论
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

    当前余额3.43前往充值 >
    需支付:10.00
    成就一亿技术人!
    领取后你会自动成为博主和红包主的粉丝 规则
    hope_wisdom
    发出的红包
    实付
    使用余额支付
    点击重新获取
    扫码支付
    钱包余额 0

    抵扣说明:

    1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
    2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

    余额充值