JetPack之ViewModel最新源码详细分析

本文会基于最新版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用来存储和管理ViewModelfactory则用来对ViewModel进行实例化。此时构建ViewModelProvider时传入的参数为所依赖的组件的ViewModelStore,也就是说,我们通过组件的ViewModelStore来对ViewModel进行存储和管理。
在这里插入图片描述

其中factory需要我们在构造的时候进行传入,ViewModelProvider给我们提供了3种实例化的方法,分别是简单工厂模式构造,通过反射构造实例,以及我们自己实现factory构造实例。

当我们使用ViewModelProvider.NewInstanceFactory()构造实例时,查看源码可看出,它实现了Factory 接口,并且在其内部的create方法中会调用我们在之前get方法中传入的viewmodelnewInstance方法。这个方法需我们自己在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 +类名。如果找到则直接返回,否则调用factorycreate方法创建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来使用viewmodellivedata常用来存储与界面相关的数据元素,当我们的activity状态改变后,界面再次从livedata中获取界面数据时,由于livedata实例由viewmodel保管,而viewmodel存储在ViewModelStore中,当前activity去获取ViewModelStore时会通过getLastNonConfigurationInstance方法恢复之前的ViewModelStore,所以状态改变前后的ViewModelStore是同一个,所以数据没有发生任何改变,从而界面相关数据不会发生丢失。

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

java将前端的json数组字符串转换为列表

记录下在前端通过ajax提交了一个json数组的字符串,在后端如何转换为列表。前端数据转化与请求varcontracts{id:'1',name:'yanggb合同1'},{id:'2',name:'yanggb合同2'},{id:'3',name:'yang

前端面试题自检 Vue 网络 浏览器 性能优化部分

框架VueMVVM是什么?ModelViewViewModel,Model表示数据模型层。view表示视图层,ViewModel是View和Model层的桥梁,数据绑定到viewModel层并自动渲染到页面中,视图变化通知viewModel层更新数据。Vue的生命周期

JetPack之ViewModel最新源码详细分析 - HelloWorld