ViewModel 源码解析
MVVM架构中,会用到ViewModel类,那么这个类原理是什么呢?有什么好处呢?
第一部分:源码文档解析
/*** ViewModel is a class that is responsible for preparing and managing the data for* an {@link android.app.Activity Activity} or a {@link androidx.fragment.app.Fragment Fragment}.//viewmodel类是用来在activity或fragment中获取和处理数据的类* It also handles the communication of the Activity / Fragment with the rest of the application//他也可以用来处理activity或fragment和应用其他模块的通信* (e.g. calling the business logic classes).* <p>* A ViewModel is always created in association with a scope (a fragment or an activity) and will* be retained as long as the scope is alive. E.g. if it is an Activity, until it is* finished.* <p>//viewmodel随着宿主的创建而创建,而且只要宿主还在他就会活着* In other words, this means that a ViewModel will not be destroyed if its owner is destroyed for a* configuration change (e.g. rotation). The new owner instance just re-connects to the existing model.* <p>//也就是说,这也就是如果宿主死了他不会跟着死的,例如activity的配置更改后重新创建,但是他还是原来的。* The purpose of the ViewModel is to acquire and keep the information that is necessary for an* Activity or a Fragment. The Activity or the Fragment should be able to observe changes in the* ViewModel. ViewModels usually expose this information via {@link LiveData} or Android Data* Binding. You can also use any observability construct from you favorite framework.* <p>//设计viewmodel的目的是获得和保持activity或fragment中的信息。宿主fragment/activity也应该观察viewmodel中数据的改变。viewmodel通常配合着databinding使用,你也可以使用其他具有观察特性的控件一起使用。* ViewModel's only responsibility is to manage the data for the UI. It <b>should never</b> access* your view hierarchy or hold a reference back to the Activity or the Fragment.* <p>//viewmodel仅仅用来管理更新UI的数据,而不应该持有view或者宿主的引用。* Typical usage from an Activity standpoint would be:* <pre>//使用示例如下:* public class UserActivity extends Activity {** {@literal @}Override* protected void onCreate(Bundle savedInstanceState) {* super.onCreate(savedInstanceState);* setContentView(R.layout.user_activity_layout);* final UserModel viewModel = new ViewModelProvider(this).get(UserModel.class);* viewModel.getUser().observe(this, new Observer<User>() {* {@literal @}Override* public void onChanged(@Nullable User data) {* // update ui.* }* });* findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {* {@literal @}Override* public void onClick(View v) {* viewModel.doAction();* }* });* }* }* </pre>** ViewModel would be:* <pre>* public class UserModel extends ViewModel {* private final MutableLiveData<User> userLiveData = new MutableLiveData<>();** public LiveData<User> getUser() {* return userLiveData;* }** public UserModel() {* // trigger user load.* }** void doAction() {* // depending on the action, do necessary business logic calls and update the* // userLiveData.* }* }* </pre>** <p>* ViewModels can also be used as a communication layer between different Fragments of an Activity.//viewmodel也可以用来用在同一个activity中不同fragment之间数据交换* Each Fragment can acquire the ViewModel using the same key via their Activity. This allows* communication between Fragments in a de-coupled fashion such that they never need to talk to* the other Fragment directly.* <pre>//每一个fragment可以通过同一个key获取到同一个viewmodel,这就实现了fragment之间的数据交换,而不是fragment之间进行数据交换。例如:* public class MyFragment extends Fragment {* public void onStart() {* UserModel userModel = new ViewModelProvider(requireActivity()).get(UserModel.class);* }* }* </pre>* </>*/
通过官方文档我可以获取如下信息:
1.viewmodel的生命周期不随着宿主的死亡而死亡
2.viewmodel 主要是用来进行数据的传递更新UI,而不允许持有宿主的引用
3.viewmodel 应该配合着具有观察者模式的类一起使用,例如MutableLiveData
4.使用示例
5.viewmodel还可以作为同一个activity中不同fragment之间数据的传递,以及示例
第二部分:源码解析(上面示例用代码)
1.获取viewmodel
UserModel viewModel = new ViewModelProvider(this).get(UserModel.class);
/*** Creates {@code ViewModelProvider}. This will create {@code ViewModels}* and retain them in a store of the given {@code ViewModelStoreOwner}.* <p>* This method will use the* {@link HasDefaultViewModelProviderFactory#getDefaultViewModelProviderFactory() default factory}* if the owner implements {@link HasDefaultViewModelProviderFactory}. Otherwise, a* {@link NewInstanceFactory} will be used.*///参数二有删减,默认就是这个参数public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {this(owner.getViewModelStore(), NewInstanceFactory.getInstance());}
先看new ViewModelProvider(this):
参数为this(例如activity /fragment),那么是任何类都可以吗?不是,这个构造器的参数为ViewModelStoreOwner,这是个接口
public interface ViewModelStoreOwner {/*** Returns owned {@link ViewModelStore}** @return a {@code ViewModelStore}*/@NonNullViewModelStore getViewModelStore();
}
我们看看他的子类有哪些:
所以我们传入fragmentactivity可以,也就是如果不是这几个类,那么我们要实现这个接口才可以,我们可以看看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();}
}
核心代码,hashmap保存viewmodel的。
第二个参数NewInstanceFactory.getInstance()
public static class NewInstanceFactory implements Factory {private static NewInstanceFactory sInstance;@NonNullstatic NewInstanceFactory getInstance() {if (sInstance == null) {sInstance = new NewInstanceFactory();}return sInstance;}@SuppressWarnings("ClassNewInstance")@NonNull@Overridepublic <T extends ViewModel> T create(@NonNull Class<T> modelClass) {return modelClass.newInstance();}}
单例模式,就一个create方法,反射创建对象。
再次回顾一下两个重要点:
第一:一个hashmap保存viewmodel
第二:一个是创建viewmodel的
下面来看new ViewModelProvider(this).get(UserModel.class); get方法:
@NonNull@MainThreadpublic <T extends ViewModel> T get(@NonNull Class<T> modelClass) {//获取全类名String canonicalName = modelClass.getCanonicalName();return get(DEFAULT_KEY + ":" + canonicalName, modelClass);}public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {//hashmap中获取ViewModel viewModel = mViewModelStore.get(key);//获取到了,说明以前创建过,则返回if (modelClass.isInstance(viewModel)) {return (T) viewModel;} //没获取到,则创建,通过上面分析的反射创建viewModel = mFactory.create(modelClass);//将创建的对象保存起来mViewModelStore.put(key, viewModel);return (T) viewModel;}
通过全类名在hashmap中查找,如果有则直接返回,没有则创建新的,并保存起来。所以说,我们多次调用get方法获取或者在同一个activity中不同fragment中使用new ViewModelProvider(getActivity()).get(UserModel.class) 都是获取到的同一个viewmodel。但是文档里说当activity的配置信息发生改变,activity重新创建的时候,viewmodel是不会重新创建的,可是我们看到的却是 mViewModelStore 是activity的一个属性,也就是说activity销毁的时候,他也应该被回收了,那应该随着新的activity的创建而创建新的viewmodel吧,文档写的貌似不对啊?其实文档写的没错,我们可以去activity里看看:
//activity配置发生改变是调用这个方法
public final Object onRetainNonConfigurationInstance() {NonConfigurationInstances nci = new NonConfigurationInstances();nci.custom = custom;nci.viewModelStore = viewModelStore;return nci;}
我们可以看到,当activity配置发生改变时,会将viewModelStore保存起来,当页面重新创建后再次获取实例时,会调用:
@NonNull@Overridepublic ViewModelStore getViewModelStore() {ensureViewModelStore();return mViewModelStore;}@SuppressWarnings("WeakerAccess") /* synthetic access */void ensureViewModelStore() {if (mViewModelStore == null) {NonConfigurationInstances nc =(NonConfigurationInstances) getLastNonConfigurationInstance();if (nc != null) {// Restore the ViewModelStore from NonConfigurationInstances//从配置改变保存的数据里恢复数据mViewModelStore = nc.viewModelStore;}if (mViewModelStore == null) {mViewModelStore = new ViewModelStore();}}}
会先在配置改变保存的数据里恢复数据,没有则再去创建。
后续viewmodel配合LiveData一起使用,我们后续再分析。这里就分析viewmodel的源码。
ViewModel 源码解析相关推荐
- Android JetPack ViewModel 源码解析
是什么? ViewModel 用来存储页面相关的数据,当页面销毁的时候,存储数据也会清楚.但是当页面发生旋转的时候,并不会清楚数据. 怎么用? UserViewModel userViewModel ...
- 5. Jetpack源码解析---ViewModel基本使用及源码解析
截止到目前为止,JetpackNote源码分析的文章已经有四篇文章了,这一系列的文章我的初衷是想仔细研究一下Jetpack,最终使用Jetpack组件写一个Demo,上一篇已经分析了LiveData, ...
- LiveData 源码解析(2.4.1 版本)
文章目录 1.LiveData 简介 2.LiveData 配置与基本用法 2.1 依赖引入与配置 2.2 基本用法 2.2.1 LiveData 简单使用 2.2.2 LiveData 扩展 2.2 ...
- Lifecycle 源码解析(2.4.1 版本)
文章目录 1.Lifecycle 简介 2.Lifecycle 配置与基本用法 2.1 依赖引入与配置 2.2 基本用法 2.2.1 Lifecycle 简单使用 2.2.2 普通 Activity ...
- 谷歌BERT预训练源码解析(二):模型构建
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/weixin_39470744/arti ...
- 谷歌BERT预训练源码解析(三):训练过程
目录 前言 源码解析 主函数 自定义模型 遮蔽词预测 下一句预测 规范化数据集 前言 本部分介绍BERT训练过程,BERT模型训练过程是在自己的TPU上进行的,这部分我没做过研究所以不做深入探讨.BE ...
- 谷歌BERT预训练源码解析(一):训练数据生成
目录 预训练源码结构简介 输入输出 源码解析 参数 主函数 创建训练实例 下一句预测&实例生成 随机遮蔽 输出 结果一览 预训练源码结构简介 关于BERT,简单来说,它是一个基于Transfo ...
- Gin源码解析和例子——中间件(middleware)
在<Gin源码解析和例子--路由>一文中,我们已经初识中间件.本文将继续探讨这个技术.(转载请指明出于breaksoftware的csdn博客) Gin的中间件,本质是一个匿名回调函数.这 ...
- Colly源码解析——结合例子分析底层实现
通过<Colly源码解析--框架>分析,我们可以知道Colly执行的主要流程.本文将结合http://go-colly.org上的例子分析一些高级设置的底层实现.(转载请指明出于break ...
最新文章
- 打patch p0 p1区别
- js中的target与currentTarget的区别转
- 走进JVM【二】理解JVM内存区域
- 豆瓣9.3的纪录片《西南联大》告诉你:大学学风应如是!
- C语言实现简单的内存管理机制
- 华为p20pro投屏到笔记本_新荣耀笔记本与微软系统合作,网友:一碰即传投屏功能还有吗...
- Android Studio Xposed模块编写(二)
- android intent sender,Android7.0以上调PendingIntent.getIntent()报错
- 63 Defi过后,人生第一次玩DAO----超级君【2020-08-22 2234】
- EBS常用查询语句_查询银行账户
- 有关网线接法的几个问题
- NanUI 无边框拖拽
- HTML旅游网站设计与实现——东江湖旅游网站6个网页HTML+CSS+JavaScript
- 车载PHY的唤醒与睡眠的正确姿势
- rv1126 数据流
- 计算机/电脑为什么拥有计算能力
- 分布式Ruby解决之道
- MyBatis02:CRUD 操作,javaee教程网上购书系统
- 微信小程序 video 视频播放卡顿
- ethercat通讯移植