Android ViewModel创建不受横竖屏切换影响原理详解
ViewModel的创建方式
在我们项目中, 引入了viewModel 做MVI 设计模式的组成部分,它是JetPack 组件库中的重要成员。今天来了解下它。
// 在 Activity 中使用 class MainActivity : AppCompatActivity() { // 使用 Activity 的作用域 private val viewModel : MainViewModel by viewModels() } // 在 Fragment 中使用 class MainFragment : Fragment() { // 使用 Activity 的作用域,与 MainActivity 使用同一个对象 val activityViewModel : MainViewModel by activityViewModels() // 使用 Fragment 的作用域 val viewModel : MainViewModel by viewModels() }
// ViewModel 创建工厂 private final Factory mFactory; // ViewModel 存储容器 private final ViewModelStore mViewModelStore; // 默认使用 NewInstanceFactory 反射创建 ViewModel public ViewModelProvider(ViewModelStoreOwner owner) { this(owner.getViewModelStore(), ... NewInstanceFactory.getInstance()); } // 自定义 ViewModel 创建工厂 public ViewModelProvider(ViewModelStoreOwner owner, Factory factory) { this(owner.getViewModelStore(), factory); } // 记录宿主的 ViewModelStore 和 ViewModel 工厂 public ViewModelProvider(ViewModelStore store, Factory factory) { mFactory = factory; mViewModelStore = store; } @NonNull @MainThread public <T extends ViewModel> T get(Class<T> modelClass) { String canonicalName = modelClass.getCanonicalName(); if (canonicalName == null) { throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels"); } // 使用类名作为缓存的 KEY return get(DEFAULT_KEY + ":" + canonicalName, modelClass); } // Fragment @NonNull @MainThread public <T extends ViewModel> T get(String key, Class<T> modelClass) { // 1. 先从 ViewModelStore 中取缓存 ViewModel viewModel = mViewModelStore.get(key); if (modelClass.isInstance(viewModel)) { return (T) viewModel; } // 2. 使用 ViewModel 工厂创建实例 viewModel = mFactory.create(modelClass); ... // 3. 存储到 ViewModelStore mViewModelStore.put(key, viewModel); return (T) viewModel; } // 默认的 ViewModel 工厂 public static class NewInstanceFactory implements Factory { private static NewInstanceFactory sInstance; @NonNull static NewInstanceFactory getInstance() { if (sInstance == null) { sInstance = new NewInstanceFactory(); } return sInstance; } @NonNull @Override public <T extends ViewModel> T create(Class<T> modelClass) { // 反射创建 ViewModel 对象 return modelClass.newInstance(); } }
可以看到:
ViewModel 实例的方法最终是通过 ViewModelProvider 完成的。ViewModelProvider 可以理解为创建 ViewModel 的工具类,它需要 2 个参数:
参数 1 ViewModelStoreOwner:
它对应于 Activity / Fragment 等持有 ViewModel 的宿主,它们内部通过 ViewModelStore 维持一个 ViewModel 的映射表,ViewModelStore 是实现 ViewModel 作用域和数据恢复的关键;
参数 2 Factory:
它对应于 ViewModel 的创建工厂,缺省时将使用默认的 NewInstanceFactory 工厂来反射创建 ViewModel 实例。
创建 ViewModelProvider 工具类后,你将通过 get() 方法来创建 ViewModel 的实例。get() 方法内部首先会通过 ViewModel 的全限定类名从映射表(ViewModelStore)中取缓存,未命中才会通过 ViewModel 工厂创建实例再缓存到映射表中。 正因为同一个 ViewModel 宿主使用的是同一个 ViewModelStore 映射表,因此在同一个宿主上重复调用 ViewModelProvider.get() 返回同一个 ViewModel 实例。
// ViewModel 本质上就是一个映射表而已 public class ViewModelStore { // <String - ViewModel> 哈希表 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(); } }
ViewModel 宿主是 ViewModelStoreOwner 接口的实现类,例如 ComponentActivity:
public class ComponentActivity extends androidx.core.app.ComponentActivity implements ContextAware, LifecycleOwner, ViewModelStoreOwner ... { // ViewModel 的存储容器 private ViewModelStore mViewModelStore; // ViewModel 的创建工厂 private ViewModelProvider.Factory mDefaultFactory; @NonNull @Override public ViewModelStore getViewModelStore() { if (mViewModelStore == null) { // 已简化过程 mViewModelStore = new ViewModelStore(); } return mViewModelStore; } }
由此,ViewModel 的创建其实跟activity 是相关联的。
ViewModel 为什么不受 Activity 横竖屏生命周期的影响
1、在 Activity 走到 onDestroy 方法时,做了判断 isChangingConfigurations
可以看到,在 ComponentActivity
构造方法中添加了生命周期的判断,当 Activity onDestroy 时,如果是发生了横竖屏切换,就不会走 getViewModelStore().clear()
,清理操作,保证了ViewModel 持有的数据还能存在。
2、在 Activity 获取 getViewModelStore 时,
通过getLastNonConfigurationInstance()
获取 NonConfigurationInstances
实例,从而得到这个实例中的 viewModelStore
而且,Activity 生命周期的变化都会走这个方法,来保证viewModelStore
不为空。
3、onRetainNonConfigurationInstance 调用
在 Activity 屏幕旋转时,onRetainNonConfigurationInstance()
在onStop
和onDestroy
之间调用
onRetainNonConfigurationInstance()
是个重要的方法
这保证了 在销毁前,viewModelStore
实例被拿到并交给 NonConfigurationInstances
实例,将 viewModelStore
赋值给他
总结
1、介绍了ViewModel的创建
- ViewModel 由创建工厂 Factory mFactory 反射创建而来,并被放到存储容器 ViewModelStore 中
2、ViewModel生命周期和横竖屏场景下不被销毁的原因
在 Activity 走到 onDestroy 方法时,做了判断 isChangingConfigurations
,如果发生横竖屏,不会清理ViewModelStore,所以ViewModel还在
销毁前 onRetainNonConfigurationInstance()
被触发,将原来的viewModelStore
赋值给 NonConfigurationInstances
实例。
从Activity重建后 调用 getViewModelStore
方法,内部 通过 getLastNonConfigurationInstance()
获取 NonConfigurationInstances
, NonConfigurationInstances
中保存的viewModelStore
实例被拿到,而viewModelStore
中保存着 ViewModel 。
以上就是Android ViewModel创建不受横竖屏切换影响原理详解的详细内容,更多关于Android ViewModel创建的资料请关注脚本之家其它相关文章!
相关文章
Android保存的文件显示到文件管理的最近文件和下载列表中的方法
这篇记录的是Android中如何把我们往存储中写入的文件,如何显示到文件管理的下载列表、最近文件列表中,需要的朋友可以参考下2020-01-01Android基于Intent实现Activity之间数据传递的方法
这篇文章主要介绍了Android基于Intent实现Activity之间数据传递的方法,结合实例形式分析了Activity之间数据传递操作的相关技巧,代码备有较为详尽的注释,需要的朋友可以参考下2016-11-11android为ListView每个Item上面的按钮添加事件
本篇文章主要介绍了android为ListView每个Item上面的按钮添加事件,有兴趣的同学可以了解一下。2016-11-11Android编程实现横竖屏切换时不销毁当前activity和锁定屏幕的方法
这篇文章主要介绍了Android编程实现横竖屏切换时不销毁当前activity和锁定屏幕的方法,涉及Android属性设置及activity操作的相关技巧,需要的朋友可以参考下2015-11-11
最新评论