ViewModel 是一种用来存储和管理UI相关数据的类。同时在配合LiveData 情况下还支持在系统配置发生改变的时候自动对数据进行保存。它可以用来管理Activity和Fragment中的数据,也可以拿来处理Fragment与Fragment之间的通信等。
ViewModel的使用
添加依赖
1 2 implementation 'androidx.appcompat:appcompat:1.0.2' implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
使用方法
1 2 3 4 5 6 7 8 9 10 11 public class PersonModel extends ViewModel { public MutableLiveData<Person> liveData = new MutableLiveData<>(); public PersonModel () { liveData.postValue(new Person()); } public void updateData () { Person value = liveData.getValue(); value.name = "test" ; liveData.setValue(value); } }
1 2 3 4 5 6 7 8 9 10 PersonModel personModel = new ViewModelProvider(this ,ViewModelProvider.AndroidViewModelFactory.getInstance(getApplication())).get(PersonModel.class); personModel.liveData.observe(this , new Observer<Person>() { @Override public void onChanged (Person person) { } }); personModel.updateData();
当页面旋转等情况下重新调用onCreate时,ViewModel其实是没有重新创建的,还是之前那个ViewModel。ViewModel只有在activity被销毁之后才会销毁,因此ViewModel也不能持有activity引用。
源码分析
创建ViewModel的过程:
1 2 3 public ViewModelProvider (@NonNull ViewModelStoreOwner owner, @NonNull Factory factory) { this (owner.getViewModelStore(), factory); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public <T extends ViewModel> T get (@NonNull String key, @NonNull Class<T> modelClass) { ViewModel viewModel = mViewModelStore.get(key); if (modelClass.isInstance(viewModel)) { return (T) viewModel; } else { if (viewModel != null ) { } } if (mFactory instanceof KeyedFactory) { viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass); } else { viewModel = (mFactory).create(modelClass); } mViewModelStore.put(key, viewModel); return (T) viewModel; }
1 2 3 4 5 6 7 @NonNull public static AndroidViewModelFactory getInstance (@NonNull Application application) { if (sInstance == null ) { sInstance = new AndroidViewModelFactory(application); } return sInstance; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public <T extends ViewModel> T create (@NonNull Class<T> modelClass) { if (AndroidViewModel.class.isAssignableFrom(modelClass)) { try { return modelClass.getConstructor(Application.class).newInstance(mApplication); } catch (NoSuchMethodException e) { throw new RuntimeException("Cannot create an instance of " + modelClass, e); } catch (IllegalAccessException e) { throw new RuntimeException("Cannot create an instance of " + modelClass, e); } catch (InstantiationException e) { throw new RuntimeException("Cannot create an instance of " + modelClass, e); } catch (InvocationTargetException e) { throw new RuntimeException("Cannot create an instance of " + modelClass, e); } } return super .create(modelClass); }
ViewModelProvider 的get方法中,首先会在mViewModelStore中根据class,获取对应的实例,如果没有,则调用AndroidViewModelFactory创建,并保存到mViewModelStore。
AndroidViewModelFactory专门用来实例化那种构造方法里面有参数的class,并且ViewModel里面可能是带Context的。它是通过newInstance(application)去实例化的.如果有带application参数则是这样实例化,如果没有带application参数的话,则还是会走newInstance()方法去构建实例。
然后看一下activity中的getViewModelStore方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public ViewModelStore getViewModelStore () { if (getApplication() == null ) { throw new IllegalStateException("Your activity is not yet attached to the " + "Application instance. You can't request ViewModel before onCreate call." ); } if (mViewModelStore == null ) { NonConfigurationInstances nc = (NonConfigurationInstances) getLastNonConfigurationInstance(); if (nc != null ) { mViewModelStore = nc.viewModelStore; } if (mViewModelStore == null ) { mViewModelStore = new ViewModelStore(); } } return mViewModelStore; }
1 2 3 public Object getLastNonConfigurationInstance () { return mLastNonConfigurationInstances != null ? mLastNonConfigurationInstances.activity :null ; }
Activity在横竖屏切换时悄悄保存了viewModelStore,放到了NonConfigurationInstances实例里面,横竖屏切换时保存了又恢复了回来,相当于ViewModel实例依然是原先的,也就因此避免了横竖屏切换时的数据丢失。
而ViewModelStore本身是就是一个简单存储数据的类。里面使用了hashmap用来存储数据:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 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()); } public final void clear () { for (ViewModel vm : mMap.values()) { vm.clear(); } mMap.clear(); } }
再和在activity的destory方法中会将mViewModelStore中的数据清除。