关于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会一直累加,不能得到释放,感兴趣的同学自己去完善,有什么疑问可以提出来
网友评论