Android Jetpack之ViewModel原理浅析及使用示例
Android Jetpack之ViewModel原理浅析及使用示例
ViewModel是Android Jetpack组件之一,它提供了一种在配置更改(如屏幕旋转)中保留数据的方法。本文将深入解析ViewModel的工作原理,并通过具体示例展示如何在Activity和Fragment之间共享数据。
一、ViewModel是什么?
ViewModel是一个具有生命周期感知能力的数据持有共享方案,能够与UI解耦。它主要用于处理以下场景:
- 屏幕旋转
- 深色模式切换
- 屏幕大小变化
- 语言或时区更改
- 字体大小或主题颜色更改
大概逻辑:
保存创建:通过
ViewModelProvider
中的ViewModelStoreOwner
与当前的Activity进行关联(可以查看到ComponentActivity
实现了ViewModelStoreOwner
这个接口),在通过get()
拿到创建的那个VM,把这个VM实例保存到ViewModelStore
中的LinkedHashMap
,当发生屏幕旋转可以到onRetainNonConfigurationInstance
这个函数中会获取之前保存在ViewModelStore
中的VM。销毁:在
ComponentActivity
的构造中看到在lifecycle
的监听中清除这个VM;当彻底销毁activity时会调用到ViewModel的onCleared
方法。
二、使用步骤
引入viewmodel库
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.1'
创建viewmodel
在viewmodel中创建num,用于演示数据在旋转屏幕的时候会不会出现重置的现象。
class MainViewModel : ViewModel() { private var num = 0 public fun add(){ num++ } public fun getNum():Int{ return num; } }
在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
)
}
四、原理浅解析
保存ViewModel
ViewModelProvider(this)
中的this
指向ViewModelStoreOwner
接口,而ViewModelStoreOwner
的实现是在Fragment
和ComponentActivity
中。
ViewModelStoreOwner
接口里面只有ViewModelStore
ViewModelStore
用来存储、清除、获取ViewModel
获取ViewModel实例
val mVm by lazy { ViewModelProvider(this).get(MainViewModel::class.java) }
获取ViewModel实例流程,将ViewModel存储到ViewModelStore中。
屏幕旋转后数据保存
在
ComponentActivity
中重新的onRetainNonConfigurationInstance
中获取到ViewModelStore实例;然后在ViewModelStore中可以获取到ViewModel实例,这才在屏幕旋转操作获取到之前的数值。ViewModel的销毁
在
ComponentActivity
构造函数里面通过Lifecycle
的监听器监听到,当前的Act处于destory销毁状态时候ViewModelStore调用'clean方法,然后在调用viewmodel的‘clean’,就达到了viewmodel跟随着activity的生命周期自动销毁。
总结
- 在VM没有的时候使用使用
onSaveInstanceState()
与onRestoreInstanceState()
进行数据的操作。在使用了Vm之后简化状态保存的成本使代码看起来更简洁。 - VM和‘Lifecycle’在‘ComponentActivity’中进行关联,达到随着Act的销毁来销毁;
- VM通过‘ViewModelProvider’类中'ViewModelStore'进行存储,然后到屏幕旋转或者系统状态发生变化后在通过‘ComponentActivity’中的‘onRetainNonConfigurationInstance’重新获取到'ViewModelStore',实现了在Act没有销毁时数据的保存;