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的数据会一直保存在内存中,如果当我们的Activity因为Config变化导致Activity发生重建的时候可以避免重复加载数据
  • 遵守单一职责设计原则(每个类应当只有一个职责)

    • 在以往的开发中,数据逻辑的变量以及对象是保存在Activity或者Fragment内的,随着页面功能以及业务逻辑的增加,代码也会变的很冗杂。

    • 在使用了ViewModel之后,我们应当避免在ViewModel中实现过多的职责。ViewModel只负责用于管理UI界面数据,同时我们可以创建一个Presenter类用于处理UI界面数据,创建一个Repository类用于保存和载入数据的唯一接口

  • ViewModel+LiveData = 响应式UI

补充一个Google推荐的Activity和ViewModel之间的结构

MVVM-第 2 页.png

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
      }

      image-20210729231644137.png

    • 第三步,此时我们可以将原来的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的生命周期

MVVM-viewmodel-life-cicle.png

ViewModel 源码解析

首先我们可以从ViewModel的初始化开始往下看。

入口–>构造函数

ViewModel有三个构造函数,分别是

/**
* Creates {@code ViewModelProvider}. This will create {@code ViewModels}
* and retain them in a store of the given {@code ViewModelStoreOwner}.
* <p>
* This method will use the
* {@link HasDefaultViewModelProviderFactory#getDefaultViewModelProviderFactory() default factory}
* if the owner implements {@link HasDefaultViewModelProviderFactory}. Otherwise, a
* {@link NewInstanceFactory} will be used.
*/
public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory ? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory() : NewInstanceFactory.getInstance());
}
/**
* Creates {@code ViewModelProvider}, which will create {@code ViewModels} via the given
* {@code Factory} and retain them in a store of the given {@code ViewModelStoreOwner}.
*
* @param owner a {@code ViewModelStoreOwner} whose {@link ViewModelStore} will be used to
* retain {@code ViewModels}
* @param factory a {@code Factory} which will be used to instantiate
* new {@code ViewModels}
*/
public ViewModelProvider(@NonNull ViewModelStoreOwner owner, @NonNull Factory factory) {
this(owner.getViewModelStore(), factory);
}
/**
* Creates {@code ViewModelProvider}, which will create {@code ViewModels} via the given
* {@code Factory} and retain them in the given {@code store}.
*
* @param store {@code ViewModelStore} where ViewModels will be stored.
* @param factory factory a {@code Factory} which will be used to instantiate
* new {@code ViewModels}
*/
public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
mFactory = factory;
mViewModelStore = store;
}

通过构造函数,我们可以看到最终我们需要的是两个参数ViewModelStore以及Factory,我们先来看ViewModelStore这个类

ViewModelStore

顾名思义,ViewModelStore是用于存储ViewModel的一个类

image-20210731112238902.png

Factory

Factory表示创建ViewModel的工厂

image-20210731213148042.png

获取ViewModel

之后我们可以通过ViewModelProvider的get方法获取ViewModel的实例,在代码中我们可以看到是根据mFactory的类型来决定采用什么方式创建ViewModel的

image-20210731215735837.png

而Factory的类型是由ViewModelStoreOwner决定的,一种是SavedStateViewModelFactory,另一种是NewInstanceFactory,我们可以看下两者create方法的不同

可能有朋友会有一个疑问,这里的判断是KeyedFactory,为什么上文中描述的并没有它,二十SavedStateViewModelFactory,我们可以再回过头看下构造函数

image-20210731222834090.png

这里的创建分为了两种,一类是默认的Factory,一类是NewInstanceFactory,我们细看下getDefaultViewModelProviderFactory这个方法

image-20210731222927305.png

其实现分别位于ComponentActivity和Fragment中,而这两个类中,创建的都是SavedStateViewModelFactory实例

image-20210731223014300.png

image-20210731223033545.png

SavedStateViewModelFactory

image-20210731220815433.png

从这个地方我们就可以看到,在我们的ViewModel是AndroidViewModel的时候,就会加入mApplication参数,通过这个参数,我们就可以在ViewModel里用到Application的Context。

NewInstanceFactory

NewInstanceFactory创建过程相对简单,通过Class的newInstance()方法直接创建ViewModel实例

image-20210731215848168.png