问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

Android Jetpack之ViewModel原理浅析及使用示例

创作时间:
作者:
@小白创作中心

Android Jetpack之ViewModel原理浅析及使用示例

引用
CSDN
1.
https://m.blog.csdn.net/weixin_41620505/article/details/145921641

ViewModel是Android Jetpack组件之一,它提供了一种在配置更改(如屏幕旋转)中保留数据的方法。本文将深入解析ViewModel的工作原理,并通过具体示例展示如何在Activity和Fragment之间共享数据。

一、ViewModel是什么?

ViewModel是一个具有生命周期感知能力的数据持有共享方案,能够与UI解耦。它主要用于处理以下场景:

  • 屏幕旋转
  • 深色模式切换
  • 屏幕大小变化
  • 语言或时区更改
  • 字体大小或主题颜色更改

大概逻辑:

  1. 保存创建:通过ViewModelProvider中的ViewModelStoreOwner与当前的Activity进行关联(可以查看到ComponentActivity实现了ViewModelStoreOwner这个接口),在通过get()拿到创建的那个VM,把这个VM实例保存到ViewModelStore中的LinkedHashMap,当发生屏幕旋转可以到onRetainNonConfigurationInstance这个函数中会获取之前保存在ViewModelStore中的VM。

  2. 销毁:在ComponentActivity的构造中看到在lifecycle的监听中清除这个VM;当彻底销毁activity时会调用到ViewModel的onCleared方法。

二、使用步骤

  1. 引入viewmodel库

    implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.1'
    
  2. 创建viewmodel

    在viewmodel中创建num,用于演示数据在旋转屏幕的时候会不会出现重置的现象。

    class MainViewModel : ViewModel() {
        private var num = 0
        public fun add(){
            num++
        }
        public fun getNum():Int{
            return num;
        }
    }
    
  3. 在activity中使用

    在act中引入viewmodel,并且创建一个act中的num用于实现屏幕旋转的时候与viewmodel中的那个num进行比对;对比后会发现VM中的num是旋转之前的值,act中的num又恢复到了初始状态0。

    class ViewmodelActivity : AppCompatActivity() {
        val mVm by lazy { ViewModelProvider(this).get(MainViewModel::class.java) }
        val tv: TextView by lazy { findViewById(R.id.testViewModelText) }
        var numAct=0
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_viewmodel)
            if (savedInstanceState == null) {
                supportFragmentManager.beginTransaction()
                    .replace(R.id.container, ViewmodelFragment.newInstance())
                    .commitNow()
            }
            tv.text = "获取的数据VM:${mVm.getNum()} ___ ActNum:${numAct}"
            findViewById<Button>(R.id.testViewModelAdd).setOnClickListener {
                mVm.add()
                numAct++
            }
            findViewById<Button>(R.id.testViewModelGet).setOnClickListener {
                tv.text = "获取的数据VM:${mVm.getNum()} ___ ActNum:${numAct}"
            }
        }
    }
    

三、Fragment与Activity共享数据

在通过创建VM时候调用ViewModelProvider中要使用requireActivity不要使用this,如果使用this就不会和act共享数据了。

private val viewModel: MainViewModel by lazy {
    ViewModelProvider(requireActivity()).get(
        MainViewModel::class.java
    )
}

四、原理浅解析

  1. 保存ViewModel

    ViewModelProvider(this)中的this指向ViewModelStoreOwner接口,而ViewModelStoreOwner的实现是在FragmentComponentActivity中。

  • ViewModelStoreOwner接口里面只有ViewModelStore
  • ViewModelStore用来存储、清除、获取ViewModel
  1. 获取ViewModel实例

    val mVm by lazy { ViewModelProvider(this).get(MainViewModel::class.java) }

    获取ViewModel实例流程,将ViewModel存储到ViewModelStore中。

  2. 屏幕旋转后数据保存

    ComponentActivity中重新的onRetainNonConfigurationInstance中获取到ViewModelStore实例;然后在ViewModelStore中可以获取到ViewModel实例,这才在屏幕旋转操作获取到之前的数值。

  3. ViewModel的销毁

    ComponentActivity构造函数里面通过Lifecycle的监听器监听到,当前的Act处于destory销毁状态时候ViewModelStore调用'clean方法,然后在调用viewmodel的‘clean’,就达到了viewmodel跟随着activity的生命周期自动销毁。

总结

  1. 在VM没有的时候使用使用onSaveInstanceState()onRestoreInstanceState()进行数据的操作。在使用了Vm之后简化状态保存的成本使代码看起来更简洁。
  2. VM和‘Lifecycle’在‘ComponentActivity’中进行关联,达到随着Act的销毁来销毁;
  3. VM通过‘ViewModelProvider’类中'ViewModelStore'进行存储,然后到屏幕旋转或者系统状态发生变化后在通过‘ComponentActivity’中的‘onRetainNonConfigurationInstance’重新获取到'ViewModelStore',实现了在Act没有销毁时数据的保存;




© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号