ViewModel
在看这个之前相信你已经了解了Lifecycle和LiveData。
1. ViewModel是什么?
ViewModel:即View(视图)Model(数据)。它是负责准备和管理和UI组件(Fragment/Activity)相关的数据类,以及UI组件通信。
2. ViewModel生命周期
上图是用Activity作为例子,左侧表示Activity的生命周期状态,右侧绿色部分表示ViewModel的生命周期范围。当屏幕旋转的时候,Activity会被recreate,Activity会经过几个生命周期方法,但是这个时候ViewModel还是之前的对象,并没有被重新创建,只有当Activity的finish()方法被调用时,ViewModel.onCleared()方法会被调用,对象才会被销毁。这张图很好的描述了是当Activity被recreate时,ViewModel的生命周期。
另外,有个注意的地方:在ViewModel中不要持有Activity的引用。为什么要注意这一点呢?从上面的图我们看到,当Activity被recreate时,ViewModel对象并没有被销毁,如果Model持有Activity的引用时就可能会导致内存泄漏。那如果你要使用到Context对象怎么办呢,那就使用ViewModel的子类AndroidViewModel吧。
3. 组件间通信
public class UserViewModel extends ViewModel {
private MutableLiveData<Integer> mProgress;
public MutableLiveData<Integer> getProgress() {
if(mProgress == null){
mProgress = new MutableLiveData<>();
}
return mProgress;
}
}
public class FragmentOne extends Fragment {
private UserViewModel mUserViewModel ;
public static FragmentOne getInstance(){
return new FragmentOne();
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//注意这里是getActivity(),不是this
ViewModelProvider viewModelProvider = new ViewModelProvider(getActivity());// create(UserViewModel.class);
mUserViewModel = viewModelProvider.get(UserViewModel.class);
}
@OnClick(R.id.btn_set_name)
void onViewClicked(View v){
switch (v.getId()){
case R.id.btn_set_value:
mUserViewModel .getProgress().setValue(20);
break;
}
}
}
public class FragmentTwo extends Fragment {
private UserViewModel mUserViewModel ;
public static FragmentTwo getInstance(){
return new FragmentTwo ();
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//注意这里是getActivity(),不是this
ViewModelProvider viewModelProvider = new ViewModelProvider(getActivity());// create(UserViewModel.class);
mUserViewModel = viewModelProvider.get(UserViewModel.class);
mUserViewModel.getProgress().observe(this, new Observer<Integer>() {
@Override
public void onChanged(Integer integer) {
//mProgressBar1.setProgress(integer);
Log.i(TAG, "mUserViewModal onChanged: " + integer);
}
});
}
}
代码逻辑很简单,首先定义一个UserViewModel 类,继承自ViewModel。然后定义两个Fragment:FragmentOne和FragmentTwo。在FragmentOne中改变UserViewModel 中LiveData保存的数据,然后在FragmentTwo中会收到数据改变的通知。这样一个过程就完成了FragmentOne和FragmentTwo之间的一次简单的通信。至于其中的原理,相信看过LiveData这篇文章话,这都不是问题了。在上面的例子中有个小陷阱:在初始化mUserViewModel 时,ViewModelProvider()构造方法传入的是Activity对象,如果你改成Fragment对象,那FragmentTwo里就只能傻等了,永远不会收到数据改变的通知。因为如果传给ViewModelProvider()构造方法对象不同时,最终得到的就不是同一个ViewModel对象,这一点也可以通过打印两个Fragment中的mUserViewModel验证。
类图
- ViewModelProviders是ViewModel工具类,该类提供了通过Fragment和Activity得到ViewModel的方法,而具体实现又是有ViewModelProvider实现的。ViewModelProvider是实现ViewModel创建、获取的工具类。在ViewModelProvider中定义了一个创建ViewModel的接口类——Factory。ViewModelProvider中有个ViewModelStore对象,用于存储ViewModel对象。
- ViewModelStore是存储ViewModel的类,具体实现是通过HashMap来保存ViewModle对象。
- ViewModel是个抽象类,里面只定义了一个onCleared()方法,该方法在ViewModel不在被使用时调用。
- ViewModel有一个子类AndroidViewModel,这个类是便于要在ViewModel中使用Context对象,因为我们前面提到是不能在ViewModel中持有Activity的引用。
- ViewModelStores是ViewModelStore的工厂方法类,它会关联HolderFragment,HolderFragment有个嵌套类——HolderFragmentManager。
时序图
在使用ViewModel的例子中,也许你会察觉得到一个ViewModel对象需要的步骤有点多啊,怎么不直接new一个出来呢?在你看到ViewModel的类图关系后,你应该就能明白了,因为是会缓存ViewModel对象的。下面以在Fragment中得到ViewModel对象为例看下整个过程的时序图。
时序图看起来比较复杂,但是它只描述了两个过程:
- 得到ViewModel对象
- HolderFragment被销毁时,ViewModel收到onCleared()通知
源码基本分析
下面分析一下为什么同一个Activity可以获取相同的UserModal实例。
public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
: NewInstanceFactory.getInstance());
}
public ViewModelProvider(@NonNull ViewModelStoreOwner owner, @NonNull Factory factory) {
this(owner.getViewModelStore(), factory);
}
发现构造函数第一个参数ViewModelStoreOwner ,即存储owner ViewModel的容器。所以如果是同一个Activity,那么ViewModelStoreOwner 是相同的。
那么怎么获取到的ViewModel的呢?继续往下看ViewModelProvider.get
@NonNull
@MainThread
public <T extends ViewModel> T get(@NonNull Class<T> modelClass) {
String canonicalName = modelClass.getCanonicalName();
if (canonicalName == null) {
throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels");
}
return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
}
@NonNull
@MainThread
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
ViewModel viewModel = mViewModelStore.get(key);
if (modelClass.isInstance(viewModel)) {
if (mFactory instanceof OnRequeryFactory) {
((OnRequeryFactory) mFactory).onRequery(viewModel);
}
return (T) viewModel;
} else {
//noinspection StatementWithEmptyBody
if (viewModel != null) {
// TODO: log a warning.
}
}
if (mFactory instanceof KeyedFactory) {
viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
} else {
viewModel = (mFactory).create(modelClass);
}
mViewModelStore.put(key, viewModel);
return (T) viewModel;
}
发现就是以UserViewModel的class拼接了一个key,然后在ViewModelStore中查询的,因为我们构造ViewModelProvider的时候是传入的相同的Activity,所以ViewModelStore是相同的,故在获取的时候将得到相同的UserViewModel.
此时我们在看看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());
}
/**
* Clears internal storage and notifies ViewModels that they are no longer used.
*/
public final void clear() {
for (ViewModel vm : mMap.values()) {
vm.clear();
}
mMap.clear();
}
}
ViewModelStore是缓存ViewModel的类,put()、get()方法用于存取ViewModel对象,另外提供了clear()方法用于清空缓存的ViewModel对象,在该方法中会调用ViewModel.onCleared()方法通知ViewModel对象不再被使用
参考博客:
https://blog.csdn.net/zhuzp_blog/article/details/78910535
推荐阅读
-
Android通过ViewModel保存数据实现多页面的数据共享功能
-
Android-ViewModel和LiveData使用详解
-
使用MVVM的常见误区(1)在ViewModel中和用户交互
-
【Medium 万赞好文】ViewModel 和 LIveData:模式 + 反模式
-
WPF 如何在ViewModel中获取ListView 的 SelectedItems
-
WPF学习笔记 MVVM模式下,ViewModel如何关闭View
-
WPF MVVM 如何在 ViewModel 中关闭界面窗口
-
【Android】ViewModel 用法及源码解析
-
领域模型(DomainModel)与视图模型(ViewModel)
-
Android开发之MVVM模式实践(一):ViewModel的封装