介绍
ViewModel 是介于 View(视图)和 Model之间的一个东西。它起到了桥梁的作用,使视图和数据既能够分离开,也能够保持通信。
ViewModel 将页面所需要的数据从页面中剥离出来,页面只需要处理用户交互和展示数据。
ViewModel 一般是通过 LiveData 或 DataBinding 两种方式来通知页面数据发生变化,更新 UI
ViewModel的生命周期
ViewModel 总是随着 Activity/Fragment 的创建而创建,随着 Activity/Fragment 的销毁而销毁
通过这张ViewModel生命周期可以看出,当屏幕发生旋转而导致Activity被销毁并重新创建时,ViewModel并没有被销毁,从而帮助我们在Activity重新创建后获取数据更新UI。它和onSaveInstanceState方法相比更有优势,因为onSaveInstanceState方法只不适合大量数据的恢复操作,只能恢复少量并且被序列化和反序列化的数据,而ViewModel不仅支持大量数据,还不需要序列化、反序列化操作。
基本用法
添加依赖:
创建ViewModel类
class LiveDataViewModel:ViewModel() {
private var timer: Timer? = null
lateinit var mListener: OnTimeChangeListener
var currentSecond = 0
fun startTiming(){
if(timer==null){
timer = Timer()
timer?.schedule(object :TimerTask(){
override fun run() {
currentSecond ++
mListener.onChanged(currentSecond)
}
},0,1000)
}
}
fun setTimeListener(listener: OnTimeChangeListener){
this.mListener = listener
}
override fun onCleared() {
super.onCleared()
timer?.cancel()
}
interface OnTimeChangeListener{
fun onChanged(second:Int)
}
}
在activity中创建viewmodel并调用里面的函数用来更新数据
class MainActivity : AppCompatActivity() {
var binding: ActivityMainBinding ? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding=DataBindingUtil.setContentView(this,R.layout.activity_main)
val bean = User("arrom", 18);
binding?.user = bean;
val model = ViewModelProvider(this)[LiveDataViewModel::class.java]
model.setTimeListener(object :LiveDataViewModel.OnTimeChangeListener{
override fun onChanged(second: Int) {
Log.d("MainActivity","onChanged${second}")
bean.name = "arrom-${second}"
bean.notifyChange()
}
})
binding?.clickBtn?.setOnClickListener {
model.startTiming()
Log.d("MainActivity","按钮点击事件")
}
}
}
源码分析
val model = ViewModelProvider(this)[LiveDataViewModel::class.java]
进入ViewModelProvider类
public constructor(
owner: ViewModelStoreOwner
) : this(owner.viewModelStore, defaultFactory(owner))
*/
public open class ViewModelProvider(
private val store: ViewModelStore,
private val factory: Factory
)
创建ViewModelProvider时必须传入2个参数:
-
ViewModelStore 用来存储和管理ViewModel
-
factory 用来对viewModel进行实例话
ViewModelProvider提供了3中实例化的方法:
- 简单工厂模式构造
- 通过反射构造实例
- 自己实现factory构造实例
NewInstanceFactory构造实例
@Suppress("SingletonConstructor")
public open class NewInstanceFactory : Factory {
@Suppress("DocumentExceptions")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return try {
//返回实例对象
modelClass.newInstance()
} catch (e: InstantiationException) {
throw RuntimeException("Cannot create an instance of $modelClass", e)
} catch (e: IllegalAccessException) {
throw RuntimeException("Cannot create an instance of $modelClass", e)
}
}
public companion object {
private var sInstance: NewInstanceFactory? = null
@JvmStatic
public val instance: NewInstanceFactory
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
get() {
if (sInstance == null) {
sInstance = NewInstanceFactory()
}
return sInstance!!
}
}
}
当我们使用NewInstanceFactory()构造实例时,查看源码可看出,它实现了Factory 接口,并且在其内部的create方法中会调用我们在之前get方法中传入的viewmodel的newInstance方法
AndroidViewModelFactory构造实例
public open class AndroidViewModelFactory(
private val application: Application
) : NewInstanceFactory() {
@Suppress("DocumentExceptions")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return if (AndroidViewModel::class.java.isAssignableFrom(modelClass)) {
try {
//通过反射获取实例对象 modelClass.getConstructor(Application::class.java).newInstance(application)
} catch (e: NoSuchMethodException) {
throw RuntimeException("Cannot create an instance of $modelClass", e)
} catch (e: IllegalAccessException) {
throw RuntimeException("Cannot create an instance of $modelClass", e)
} catch (e: InstantiationException) {
throw RuntimeException("Cannot create an instance of $modelClass", e)
} catch (e: InvocationTargetException) {
throw RuntimeException("Cannot create an instance of $modelClass", e)
}
} else super.create(modelClass)
}
public companion object {
internal fun defaultFactory(owner: ViewModelStoreOwner): Factory =
if (owner is HasDefaultViewModelProviderFactory)
owner.defaultViewModelProviderFactory else instance
internal const val DEFAULT_KEY = "androidx.lifecycle.ViewModelProvider.DefaultKey"
private var sInstance: AndroidViewModelFactory? = null
@JvmStatic
public fun getInstance(application: Application): AndroidViewModelFactory {
if (sInstance == null) {
sInstance = AndroidViewModelFactory(application)
}
return sInstance!!
}
}
}
自己实现factory构造实例
public interface Factory {
public fun <T : ViewModel> create(modelClass: Class<T>): T
}
public open operator fun <T : ViewModel> get(key: String, modelClass: Class<T>): T {
var viewModel = store[key]
if (modelClass.isInstance(viewModel)) {
(factory as? OnRequeryFactory)?.onRequery(viewModel)
return viewModel as T
} else {
@Suppress("ControlFlowWithEmptyBody")
if (viewModel != null) {
// TODO: log a warning.
}
}
viewModel = if (factory is KeyedFactory) {
factory.create(key, modelClass)
} else {
factory.create(modelClass)
}
store.put(key, viewModel)
return viewModel
}
我们在构造ViewModelProvider时,会将我们传入的这2个参数进行保存,当我们调用get方法时,该方法内首先会从ViewModelStore中通过key去获取ViewModel ,这个key就是上面的DEFAULT_KEY +类名。如果找到则直接返回,否则调用factory的create方法创建ViewModel实例放入ViewModelStore进行管理和保存并返回该实例
进入ViewModelStore类
public class ViewModelStore {
private final HashMap<String, ViewModel> mMap = new HashMap<>();
final void put(String key, ViewModel viewModel) {
ViewModel oldViewModel = mMap.put(key, viewModel);
if (oldViewModel != null) {
oldViewModel.onCleared();
}
}
final ViewModel get(String key) {
return mMap.get(key);
}
Set<String> keys() {
return new HashSet<>(mMap.keySet());
}
public final void clear() {
for (ViewModel vm : mMap.values()) {
vm.clear();
}
mMap.clear();
}
}
通过hashmap来对viewmodel进行管理
ViewModelStoreOwner 分析
在获取ViewModelStore的时候是通过一个ViewModelStoreOwner的接口来获取的
我们创建ViewModelProvider的时候传入的是fragment或者是activity,而fragment其实最终也是调用的activity的getViewModelStore方法
ComponentActivity类中
public ViewModelStore getViewModelStore() {
if (getApplication() == null) {
throw new IllegalStateException("Your activity is not yet attached to the "
+ "Application instance. You can't request ViewModel before onCreate call.");
}
//如果当前组件的mViewModelStore 为空,则从NonConfigurationInstances 中去获取
if (mViewModelStore == null) {
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
// Restore the ViewModelStore from NonConfigurationInstances
mViewModelStore = nc.viewModelStore;
}
//如果在NonConfigurationInstances 中没有找到,则创建一个新的ViewModelStore并保存起来
if (mViewModelStore == null) {
mViewModelStore = new ViewModelStore();
}
}
return mViewModelStore;
}
通过getViewModelStore去获取一个ViewModelStore ,当ViewModelStore 获取为空时,会调用getLastNonConfigurationInstance方法去恢复状态改变前的NonConfigurationInstances,和这个方法对应的是onRetainNonConfigurationInstance,该方法会在组件状态改变时去保存数据信息,由系统调用。
static final class NonConfigurationInstances {
Object custom;
ViewModelStore viewModelStore;
}
viewModelStore的赋值的时机
public final Object onRetainNonConfigurationInstance() {
Object custom = onRetainCustomNonConfigurationInstance();
ViewModelStore viewModelStore = mViewModelStore;
if (viewModelStore == null) {
// No one called getViewModelStore(), so see if there was an existing
// ViewModelStore from our last NonConfigurationInstance
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
viewModelStore = nc.viewModelStore;
}
}
if (viewModelStore == null && custom == null) {
return null;
}
NonConfigurationInstances nci = new NonConfigurationInstances();
nci.custom = custom;
nci.viewModelStore = viewModelStore;
return nci;
}
在组件的状态发生改变时,会去尝试获取viewModelStore,并将viewModelStore保存在NonConfigurationInstances 中并返回。
在onRetainNonConfigurationInstance中会对viewModelStore进行保存,在getLastNonConfigurationInstance中会对viewModelStore进行恢复
小结
当我们在activity中去实例化ViewModelProvider时,所依赖的activity在其内部会调用getViewModelStore方法去获取一个ViewModelStore,如果没有则创建一个,并保存起来,然后调用ViewModelProvider.get方法将我们的viewmodel实例通过ViewModelStore进行保存管理,当我们的activity的状态发生改变,如旋转屏幕,此时系统会调用onRetainNonConfigurationInstance方法,在这个方法内会将我们的ViewModelStore进行保存。