ViewModel原理解析

关于viewmodel创建

从Android官网可以看到创建ViewModel的方法ViewModelProviders.of()已经被弃用了,我们需要创建ViewModel,只用使用new ViewModelProvider的方式。那么我们应该如何使用呢?


image.png

使用new ViewModelProvider方法创建ViewModel

从官方给的文档来看,ViewModelProvider有两个构造方法,如下图


image.png
  • ViewModelStore内部维护了一个HashMap去保存ViewModel
  • ViewModelStoreOwner实际是一个接口提供了ViewModelStore的获取方法,ComponentActivity和Fragment均实现了该接口,故而在activity或者fragment中传this即可
  • factory接口提供create方法,用于创建ViewModel

3个创建方法的目的都是为了给mFactory和mViewModelStore赋值,那这2个参数具体用在哪个地方?
带着疑问,我们来到viewmodel的 创建方法:

new ViewModelProvider(requireActivity()).get(xxx.class)

看下get方法:


image.png

还没发现有什么看点,Ctrl+鼠标左键,继续往下看:

image.png

注意看红框部分,我们发现调用mFactory的create方法创建出viewmodel实例,然后将该实例缓存到mViewModelStore容器中,流程理清楚了,接下来看看几个例子:

新建个TestVm 类:

  public class TestVm extends ViewModel {}

在activity中创建:

TestVm acVm = new ViewModelProvider(this).get(TestVm.class);
 Log.d("acVm", acVm.toString());

在fragment中创建:

TestVm fgVm = new ViewModelProvider(requireActivity()).get(TestVm.class);
Log.d("vmTest", fgVm.toString());

logcat输出日志:

image.png

发现在activity和fragment中创建的viewmodel是同一个对象,为什么?


image.png

点进构造方法查看:


image.png

发现第一个参数是owner.getViewModelStore()
前面说了,ComponentActivity实现了ViewModelStoreOwner接口,因此拿到的是


image.png

接下来看第二个参数:owner一定是 instanceof HasDefaultViewModelProviderFactory 的,因为ComponentActivity也实现了HasDefaultViewModelProviderFactory这个接口


image.png

image.png

这样就明了了,在activity中创建传this和在fragment中创建传requireActivity()实际走的都是同一套流程,在fragment中拿到的就是从activity的ViewModelStore容器中拿到的那个实例,因此2个对象是同一个,但是如果在fragment中这样创建:

TestVm fgVm = new ViewModelProvider(this).get(TestVm.class);
Log.d("vmTest", fgVm.toString());

this指的是当前fragment,它实际是走fragment的创建流程,viewmodel是从fragment的ViewModelStore容器中拿的,所以是2个不同的对象

拓展

问:viewmodel可以作用于多个activity吗?
答:不能
问:有办法吗?
答案肯定是有的,在了解了viewmodel的整个创建流程后,我们知道,要想实现viewmodel用于多个activity之间通信,首先对象肯定必须是同一个,我们可以在baseac里面去定义一个静态的HashMap去存储ViewModelStoreOwner

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    inject();
}
private static Map storeMap = new HashMap<>();
void inject() {
    String key = "测试key,可以考虑用注解来实现";
    for (Field field : this.getClass().getDeclaredFields()) {
        ViewModelStoreOwner storeOwner;
        if (storeMap.containsKey(key)) {
            storeOwner = storeMap.get(key);
        } else {
            storeOwner = this;
            storeMap.put(key,storeOwner);
        }
        Class modelClass = (Class) field.getType();
        ViewModel viewModel = new ViewModelProvider(storeOwner, new VMFactory()).get(modelClass);
        try {
            field.set(this, viewModel);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}

简单写个demo,目前有个问题就是storeMap的key可以考虑用注解的形式,还有就是storeMap回收问题,发现随着页面增多,storeMap会一直累加,不能得到释放,感兴趣的同学自己去完善,有什么疑问可以提出来

示例

你可能感兴趣的:(ViewModel原理解析)