Android MVVM Study Note–ViewModel学习总结&&源码解析
https://developer.android.com/topic/libraries/architecture/viewmodel
简介
ViewModel是Android Jetpack组件库中的一个组件,其主要特性为ViewModel自身同Activity、Fragment、Service的生命周期绑定,在组件的声明周期内数据会一直保存在内存中。
简而言之,它就是一个具有生命感知能力的用于存储和管理数据的组件
ViewModel的优势
与UI层的低耦合
- 在MVVM模式的设计中,ViewModel只需要关注数据和业务逻辑而不需要处理UI上的逻辑
Fragment间共享ViewModel的数据
- 使用ViewModel共享数据可以免去Fragment之前相互回调,官方文档介绍如下:
- https://developer.android.google.cn/topic/libraries/architecture/viewmodel#sharing
- 使用ViewModel可以让Fragment之间在进行数据共享的时候,不需要对彼此进行引用,也不需要依赖于同一个parent activity
数据一直保存在内存中
- ViewModel的数据会一直保存在内存中,如果当我们的Activity因为Config变化导致Activity发生重建的时候可以避免重复加载数据
遵守单一职责设计原则(每个类应当只有一个职责)
在以往的开发中,数据逻辑的变量以及对象是保存在Activity或者Fragment内的,随着页面功能以及业务逻辑的增加,代码也会变的很冗杂。
在使用了ViewModel之后,我们应当避免在ViewModel中实现过多的职责。ViewModel只负责用于管理UI界面数据,同时我们可以创建一个Presenter类用于处理UI界面数据,创建一个Repository类用于保存和载入数据的唯一接口
ViewModel+LiveData = 响应式UI
补充一个Google推荐的Activity和ViewModel之间的结构
ViewModel的使用
我们可以通过以下步骤来创建并使用ViewModel
第一步,继承自ViewModel类创建自己的ViewModel
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
class CountChangeViewModel : ViewModel() {
var count = MutableLiveData<Int>()
init {
count.value = 0
}
}第二步,在Activity的onCreate方法中,使用ViewModelProvider这个类来获取对应的ViewModel
val colorChangeViewModel = ViewModelProvider(this).get(CountChangeViewModel::class.java)
第三步,此时我们就可以通过获取到的ViewModel对象来访问具体的数据了
val currentCount = colorChangeViewModel.count
ViewModel的构造函数
ViewModel的默认构造函数是没有参数的,在实际的开发过程中可能有些具体的业务希望ViewModel能有带参数的构造函数
默认构造函数,在获取ViewModel之后立刻给变量赋值
使用ViewModelFactory‘来创建自定义构造函数
第一步,我们可以继承自ViewModelProvider.NewInstanceFactory() 创建属于我们自己的Factory
class ViewModelFactory constructor(private val repository: String) : ViewModelProvider.NewInstanceFactory() { }
第二步,我们需要覆写里面的create方法
class ViewModelFactory constructor(private val repository: String) : ViewModelProvider.NewInstanceFactory() {
override fun <T : ViewModel?> create(modelClass: Class<T>): T = with(modelClass) {
when {
isAssignableFrom(CountChangeViewModel::class.java) -> CountChangeViewModel(repository)
else -> {
throw IllegalArgumentException(
"Unknown ViewModel class: ${modelClass.name}")
}
}
} as T
}第三步,此时我们可以将原来的ViewModel的使用方式换成
val factory = ViewModelFactory("test")
val colorChangeViewModel = ViewModelProvider(this, factory).get(CountChangeViewModel::class.java)
使用ViewModel是需要避免的误区
- Context不能传给ViewModel
- ViewModel中不应当存在Activity、Fragment甚至View的引用,因为其本身的存在的时候对应的组件可能会被销毁或者创建很多次,如果此时对应的组件被销毁,且ViewModel保存了他们的引用,就会出现内存泄漏问题
如果在开发过程中需要用到Application的Context,我们可以将我们的ViewModel继承自 AndroidViewModel,这个类已经含有了Application引用
- ViewModel是不可以替代onSaveInstanceState的,因为ViewModel会在应用程序被系统停止的时候销毁
我们应该尽量将UI界面所需的数据保存在ViewModel中,而onSaveInstanceState可以用于保存小量级的数据,应用被强制关闭之后可以使用它来恢复UI界面
ViewModel的生命周期
ViewModel 源码解析
首先我们可以从ViewModel的初始化开始往下看。
入口–>构造函数
ViewModel有三个构造函数,分别是
/** |
/** |
/** |
通过构造函数,我们可以看到最终我们需要的是两个参数ViewModelStore以及Factory,我们先来看ViewModelStore这个类
ViewModelStore
顾名思义,ViewModelStore是用于存储ViewModel的一个类
Factory
Factory表示创建ViewModel的工厂
获取ViewModel
之后我们可以通过ViewModelProvider的get方法获取ViewModel的实例,在代码中我们可以看到是根据mFactory的类型来决定采用什么方式创建ViewModel的
而Factory的类型是由ViewModelStoreOwner决定的,一种是SavedStateViewModelFactory,另一种是NewInstanceFactory,我们可以看下两者create方法的不同
可能有朋友会有一个疑问,这里的判断是KeyedFactory,为什么上文中描述的并没有它,二十SavedStateViewModelFactory,我们可以再回过头看下构造函数
这里的创建分为了两种,一类是默认的Factory,一类是NewInstanceFactory,我们细看下getDefaultViewModelProviderFactory这个方法
其实现分别位于ComponentActivity和Fragment中,而这两个类中,创建的都是SavedStateViewModelFactory实例
SavedStateViewModelFactory
从这个地方我们就可以看到,在我们的ViewModel是AndroidViewModel的时候,就会加入mApplication参数,通过这个参数,我们就可以在ViewModel里用到Application的Context。
NewInstanceFactory
NewInstanceFactory创建过程相对简单,通过Class的newInstance()方法直接创建ViewModel实例