ViewModel用及出现背景
👶官方说法:
ViewModel
类旨在以注重生命周期的方式存储和管理界面相关的数据。ViewModel
类让数据可在发生屏幕旋转等配置更改后继续留存。面控制器经常需要进行可能需要一些时间才能返回的异步调用。界面控制器需要管理这些调用,并确保系统在其销毁后清理这些调用以避免潜在的内存泄漏。此项管理需要大量的维护工作,并且在为配置更改重新创建对象的情况下,会造成资源的浪费,因为对象可能需要重新发出已经发出过的调用。
诸如 Activity 和 Fragment 之类的界面控制器主要用于显示界面数据、对用户操作做出响应或处理操作系统通信(如权限请求)。如果要求界面控制器也负责从数据库或网络加载数据,那么会使类越发膨胀。为界面控制器分配过多的责任可能会导致单个类尝试自己处理应用的所有工作,而不是将工作委托给其他类。以这种方式为界面控制器分配过多的责任也会大大增加测试的难度。
♑️说人话
发生屏幕旋转后数据仍然需要保存
界面的数据请求需要考虑在activity/Fragment生命周期回调的问题,避免数据回来时,界面已经销毁
activity/Fragment 既要承担展示UI,又要承担获取数据(netWork/database),职责不单一,无法编写测试代码
ViewModel实际使用
-
实际使用过程中还是配合着LiveData使用,使用示例(官网)
- viewModel的创建
<pre class="md-fences md-end-block md-fences-with-lineno ty-contain-cm modeLoaded" spellcheck="false" lang="kotlin" cid="n26" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px 0px; margin-bottom: 0px; margin-top: 0px; width: inherit;"> class MyViewModel : ViewModel() {
private val users: MutableLiveData<List<User>> by lazy {
MutableLiveData<List<User>>().also {
loadUsers()
}
}fun getUsers(): LiveData<List<User>> {
return users
}private fun loadUsers() {
// 异步获取数据.
}
}</pre>-
activity中使用
<pre class="md-fences md-end-block md-fences-with-lineno ty-contain-cm modeLoaded" spellcheck="false" lang="kotlin" cid="n30" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px 0px; margin-bottom: 15px; margin-top: 15px; width: inherit;"> class MyActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
//使用委托来加载viewModel
val model: MyViewModel by viewModels()model.getUsers().observe(this, Observer<List<User>>{ users ->
// update UI
})
}
}</pre>注意:
所有者 Activity销毁时,框架会调用
ViewModel
对象的onCleared()
方法,以便它可以清理资源。ViewModel对象存在的时间比视图activity或者fragment 的特定实例存在的时间更长,避免内存泄漏,viewModel不可持有activity/fragment对象的引用
viewModel中可以持有
liveData
对象,但是不能观察livedata
对象数据的更改若是需要使用application Context,需要继承Android viewModel,实际使用过程中获取application context的方法有很多不一定需要这个
-
关于ViewModel的创建的其它问题
委托的函数其实是通过 ComponentActivity的扩展函数的方式来创建的,本质还是调用ViewModelProvider的方法
- viewmodels()方法
<pre class="md-fences md-end-block md-fences-with-lineno ty-contain-cm modeLoaded" spellcheck="false" lang="kotlin" cid="n48" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px 0px; margin-bottom: 0px; margin-top: 0px; width: inherit;"> @MainThread
inline fun <reified VM : ViewModel> ComponentActivity.viewModels(
noinline factoryProducer: (() -> Factory)? = null
): Lazy<VM> {
val factoryPromise = factoryProducer ?: {
defaultViewModelProviderFactory
}return ViewModelLazy(VM::class, { viewModelStore }, factoryPromise)
}</pre>- ViewModelLazy类
<pre class="md-fences md-end-block md-fences-with-lineno ty-contain-cm modeLoaded" spellcheck="false" lang="kotlin" cid="n52" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px 0px; margin-bottom: 15px; margin-top: 15px; width: inherit;"> public class ViewModelLazy<VM : ViewModel> (
private val viewModelClass: KClass<VM>,
private val storeProducer: () -> ViewModelStore,
private val factoryProducer: () -> ViewModelProvider.Factory
) : Lazy<VM> {
private var cached: VM? = nulloverride val value: VM
get() {
val viewModel = cached
return if (viewModel == null) {
val factory = factoryProducer()
val store = storeProducer()
//调用viewModelProvider的方法
ViewModelProvider(store, factory).get(viewModelClass.java).also {
cached = it
}
} else {
viewModel
}
}override fun isInitialized(): Boolean = cached != null
}</pre>-
fragment使用该委托函数创建viewModel是注意:当需要在界面之间共享数据时,使用activity viewModel
<pre class="md-fences md-end-block md-fences-with-lineno ty-contain-cm modeLoaded" spellcheck="false" lang="kotlin" cid="n56" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px 0px; margin-bottom: 15px; margin-top: 15px; width: inherit;"> @MainThread
inline fun <reified VM : ViewModel> Fragment.activityViewModels(
noinline factoryProducer: (() -> Factory)? = null
) = createViewModelLazy(VM::class, { requireActivity().viewModelStore },
factoryProducer ?: { requireActivity().defaultViewModelProviderFactory })
</pre>这种场景一般出现在两个Ffragment之间存在数据交互时,它们能收到相同的
SharedViewModel
实例(其范围限定为该 Activity)。