美文网首页
ViewModel原理解析

ViewModel原理解析

作者: ana生 | 来源:发表于2022-03-03 18:05 被阅读0次

    关于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<String, ViewModelStoreOwner> 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<ViewModel> modelClass = (Class<ViewModel>) 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原理解析

          本文链接:https://www.haomeiwen.com/subject/bvlurrtx.html