本文会基于最新版ViewModel使用方法与源码进行详细分析,从注册到实现ViewModel界面数据如何保存与管理全部涉及。
**
简介:
**ViewModel是JetPack系列库之一,它用来对组件的界面数据进行管理,且当组件的状态发生改变时数据依然留存。
优点:1.当所依赖组件的状态发生改变时,例如屏幕旋转等,界面数据不会发生改变
2.实现MVVM架构的基础,在日常开发中,ViewModel就是MVVM架构的VM层,它相当于MVP架构的present,我们可以将业务逻辑以及数据交互的部分放入ViewModel中。
源码分析:
版本:viewmodel:1.1.1
此处使用的示例代码与livedata源码分析的相同,不影响流程分析
首先构造一个继承于ViewModel的类

接着创建一个factory 实例传入ViewModelProvider(this,factory )中去并调用get方法将我们的ViewModel作为参数传入。
此处需要注意的是在以前的版本中直接通过 ViewModelProviders.of(this).get()方法来完成这个操作的。在最新的版本中已经取消了of方法,并且构造方法必须要传入2个参数

查看ViewModelProvider的构造方法可看出构造ViewModelProvider时必须传入2个参数才行,其中第一个参数为ViewModelStore,第二个参数为factory,
ViewModelStore用来存储和管理ViewModel,factory则用来对ViewModel进行实例化。此时构建ViewModelProvider时传入的参数为所依赖的组件的ViewModelStore,也就是说,我们通过组件的ViewModelStore来对ViewModel进行存储和管理。

其中factory需要我们在构造的时候进行传入,ViewModelProvider给我们提供了3种实例化的方法,分别是简单工厂模式构造,通过反射构造实例,以及我们自己实现factory构造实例。
当我们使用ViewModelProvider.NewInstanceFactory()构造实例时,查看源码可看出,它实现了Factory 接口,并且在其内部的create方法中会调用我们在之前get方法中传入的viewmodel的newInstance方法。这个方法需我们自己在viewmodel中去实现,用来返回viewmodel实例,如果实例化出错则会抛出异常
1 public static class NewInstanceFactory implements Factory { 2 3 @SuppressWarnings("ClassNewInstance") 4 @NonNull 5 @Override 6 public <T extends ViewModel> T create(@NonNull Class<T> modelClass) { 7 //noinspection TryWithIdenticalCatches 8 try { 9 //返回modelClass实例对象 10 return modelClass.newInstance(); 11 } catch (InstantiationException e) { 12 throw new RuntimeException("Cannot create an instance of " + modelClass, e); 13 } catch (IllegalAccessException e) { 14 throw new RuntimeException("Cannot create an instance of " + modelClass, e); 15 } 16 } 17 }
使用AndroidViewModelFactory 方式反射生成ViewModel,该Factory使用单例模式进行构建 ,所以它的生命周期等同于应用程序的生命周期,且只有一个实例,推荐使用此模式来获取factory,当它的create方法被调用时,会通过application利用反射创建一个ViewModel实例并返回。
1 public static class AndroidViewModelFactory extends ViewModelProvider.NewInstanceFactory { 2 3 private static AndroidViewModelFactory sInstance; 4 @NonNull 5 public static AndroidViewModelFactory getInstance(@NonNull Application application) { 6 if (sInstance == null) { 7 sInstance = new AndroidViewModelFactory(application); 8 } 9 return sInstance; 10 } 11 12 private Application mApplication; 13 14 public AndroidViewModelFactory(@NonNull Application application) { 15 //构造时进行传入 16 mApplication = application; 17 } 18 19 @NonNull 20 @Override 21 public <T extends ViewModel> T create(@NonNull Class<T> modelClass) { 22 if (AndroidViewModel.class.isAssignableFrom(modelClass)) { 23 //noinspection TryWithIdenticalCatches 24 try { 25 //通过反射创建viewmodel 26 return modelClass.getConstructor(Application.class).newInstance(mApplication); 27 } catch (NoSuchMethodException e) { 28 throw new RuntimeException("Cannot create an instance of " + modelClass, e); 29 } catch (IllegalAccessException e) { 30 throw new RuntimeException("Cannot create an instance of " + modelClass, e); 31 } catch (InstantiationException e) { 32 throw new RuntimeException("Cannot create an instance of " + modelClass, e); 33 } catch (InvocationTargetException e) { 34 throw new RuntimeException("Cannot create an instance of " + modelClass, e); 35 } 36 } 37 return super.create(modelClass); 38 } 39 }
第三种方式就是直接实现Factory接口,并自己实现create方法,我们需要在该create方法内,返回viewmodel的实例
1 /** 2 * Implementations of {@code Factory} interface are responsible to instantiate ViewModels. 3 */ 4 public interface Factory { 5 /** 6 * Creates a new instance of the given {@code Class}. 7 * <p> 8 * 9 * @param modelClass a {@code Class} whose instance is requested 10 * @param <T> The type parameter for the ViewModel. 11 * @return a newly created ViewModel 12 */ 13 @NonNull 14 <T extends ViewModel> T create(@NonNull Class<T> modelClass); 15 }
当我们在构造ViewModelProvider时,会将我们传入的这2个参数进行保存,
1 public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) { 2 mFactory = factory; 3 mViewModelStore = store; 4 }
当我们调用get方法时
1 public <T extends ViewModel> T get(@NonNull Class<T> modelClass) { 2 //获取到ViewModel的类名 3 String canonicalName = modelClass.getCanonicalName(); 4 if (canonicalName == null) { 5 throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels"); 6 } 7 //再次进行回调 8 return get(DEFAULT_KEY + ":" + canonicalName, modelClass); 9 } 10
在该方法内首先会从ViewModelStore中通过key去获取ViewModel ,这个key就是上面的DEFAULT_KEY +类名。如果找到则直接返回,否则调用factory的create方法创建ViewModel实例放入ViewModelStore进行管理和保存并返回该实例
1 public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) { 2 //从ViewModelStore中获取viewModel 3 ViewModel viewModel = mViewModelStore.get(key); 4 //如果找到则直接返回 5 if (modelClass.isInstance(viewModel)) { 6 //noinspection unchecked 7 return (T) viewModel; 8 } else { 9 //noinspection StatementWithEmptyBody 10 if (viewModel != null) { 11 // TODO: log a warning. 12 } 13 } 14 15 if (mFactory instanceof KeyedFactory) { 16 viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass); 17 } else { 18 viewModel = (mFactory).create(modelClass); 19 } 20 mViewModelStore.put(key, viewModel); 21 //noinspection unchecked 22 return (T) viewModel; 23 }
ViewModelStore 内部通过hashmap来对viewmodel进行管理,
1public class ViewModelStore { 2 3 private final HashMap<String, ViewModel> mMap = new HashMap<>(); 4 5 final void put(String key, ViewModel viewModel) { 6 ViewModel oldViewModel = mMap.put(key, viewModel); 7 if (oldViewModel != null) { 8 oldViewModel.onCleared(); 9 } 10 } 11 12 final ViewModel get(String key) { 13 return mMap.get(key); 14 } 15 16 Set<String> keys() { 17 return new HashSet<>(mMap.keySet()); 18 } 19 20 /** 21 * Clears internal storage and notifies ViewModels that they are no longer used. 22 */ 23 public final void clear() { 24 for (ViewModel vm : mMap.values()) { 25 vm.clear(); 26 } 27 mMap.clear(); 28 } 29}
到此处为止源码部分分析完毕,如何触发viewmodel对组件界面的状态保存继续分析
上面说过在构建ViewModelProvider时传入的ViewModelStore来自于构建它所传入的this,也就是当前组件,我们知道ViewModelStore起到了对viewmodel的管理作用,viewmodel的获取和存储都是通过它来完成。
1 public ViewModelProvider(@NonNull ViewModelStoreOwner owner, @NonNull Factory factory) { 2 在这里调用了组件的owner.getViewModelStore()方法 3 this(owner.getViewModelStore(), factory); 4 } 5
追踪getViewModelStore方法,发现它是一个ViewModelStoreOwner 接口中的一个方法
1public interface ViewModelStoreOwner { 2 /** 3 * Returns owned {@link ViewModelStore} 4 * 5 * @return a {@code ViewModelStore} 6 */ 7 @NonNull 8 ViewModelStore getViewModelStore(); 9}
这里发现它的实现类有很多个,我们一般在ViewModelProvider中传入的是fragment或者activity,而fragment其实最终也是调用的activity的getViewModelStore方法这里查看activity如何处理。

发现在activity的父类ComponentActivity中有这个方法的实现,通过getViewModelStore去获取一个ViewModelStore ,当ViewModelStore 获取为空时,会调用getLastNonConfigurationInstance方法去恢复状态改变前的NonConfigurationInstances,和这个方法对应的是onRetainNonConfigurationInstance,该方法会在组件状态改变时去保存数据信息,由系统调用。
1 public ViewModelStore getViewModelStore() { 2 if (getApplication() == null) { 3 throw new IllegalStateException("Your activity is not yet attached to the " 4 + "Application instance. You can't request ViewModel before onCreate call."); 5 } 6 //如果当前组件的mViewModelStore 为空,则从NonConfigurationInstances 中去获取 7 if (mViewModelStore == null) { 8 //返回之前 9 NonConfigurationInstances nc = 10 (NonConfigurationInstances) getLastNonConfigurationInstance(); 11 if (nc != null) { 12 // Restore the ViewModelStore from NonConfigurationInstances 13 mViewModelStore = nc.viewModelStore; 14 } 15 //如果在NonConfigurationInstances 中没有找到,则创建一个新的ViewModelStore并保存起来 16 if (mViewModelStore == null) { 17 mViewModelStore = new ViewModelStore(); 18 } 19 } 20 return mViewModelStore; 21 }
在代码中ViewModelStore会从NonConfigurationInstances 中去尝试获取,我们查看viewModelStore的赋值时机即可
1 static final class NonConfigurationInstances { 2 Object custom; 3 ViewModelStore viewModelStore; 4 }
查看发现它的赋值时机就是在onRetainNonConfigurationInstance中完成的,也就是说在组件的状态发生改变时,会去尝试获取viewModelStore,并将viewModelStore保存在NonConfigurationInstances 中并返回。
也就是在onRetainNonConfigurationInstance中会对viewModelStore进行保存,在getLastNonConfigurationInstance中会对viewModelStore进行恢复

具体流程:
当我们在activity中去实例化ViewModelProvider时,所依赖的activity在其内部会调用getViewModelStore方法去获取一个ViewModelStore,如果没有则创建一个,并保存起来,然后调用ViewModelProvider.get方法将我们的viewmodel实例通过ViewModelStore进行保存管理,当我们的activity的状态发生改变,如旋转屏幕,此时系统会调用onRetainNonConfigurationInstance方法,在这个方法内会将我们的ViewModelStore进行保存。
我们一般会搭配livedata来使用viewmodel,livedata常用来存储与界面相关的数据元素,当我们的activity状态改变后,界面再次从livedata中获取界面数据时,由于livedata实例由viewmodel保管,而viewmodel存储在ViewModelStore中,当前activity去获取ViewModelStore时会通过getLastNonConfigurationInstance方法恢复之前的ViewModelStore,所以状态改变前后的ViewModelStore是同一个,所以数据没有发生任何改变,从而界面相关数据不会发生丢失。