简介
ViewModel 类旨在以注重生命周期的方式存储和管理界面相关的数据,让数据可在发生屏幕旋转等配置更改后继续留存。
ViewModel有两个特性。
- 当配置发生改变时(例如:旋转屏幕),重新创建的Activity能够通过ViewModel将数据还原回来;
- 当按返回键或者调用finish方法时,ViewModel能够感知到onDestroy事件,同时将ViewModel保存的
Closeable对象关闭掉(例如:主动关闭协程)
当屏幕旋转时,会调用Activity的onRetainNonConfigurationInstance方法。ViewModel组件正是通过该方法将ViewModel保存起来,给重建的Activity使用。
1、数据保持与恢复
对于简单的数据,Activity 可以使用 onSaveInstanceState() 方法从 onCreate() 中的捆绑包恢复其数据,但此方法仅适合可以序列化再反序列化的少量数据,而不适合数量可能较大的数据,如用户列表或位图。
ViewModel 生命周期:
ViewModel对象存在的时间范围是获取ViewModel时传递给ViewModelProvider的Lifecycle。
ViewModel将一直留在内存中,直到限定其存在时间范围的Lifecycle永久消失:对于 activity,是在 activity 完成时;而对于 fragment,是在 fragment 分离时。
注意:ViewModel 不能引用视图、Lifecycle 或可能存储对 Activity 上下文的引用的任何类。
2、异步回调问题
界面控制器经常需要进行可能需要一些时间才能返回的异步调用。界面控制器需要管理这些调用,并确保系统在其销毁后清理这些调用以避免潜在的内存泄漏。此项管理需要大量的维护工作,并且在为配置更改重新创建对象的情况下,会造成资源的浪费,因为对象可能需要重新发出已经发出过的调用。
3、分担 UI controller 负担
诸如
Activity和Fragment之类的界面控制器主要用于显示界面数据、对用户操作做出响应或处理操作系统通信(如权限请求)。如果要求界面控制器也负责从数据库或网络加载数据,那么会使类越发膨胀。为界面控制器分配过多的责任可能会导致单个类尝试自己处理应用的所有工作,而不是将工作委托给其他类。以这种方式为界面控制器分配过多的责任也会大大增加测试的难度。
从界面控制器逻辑中分离出视图数据所有权的操作更容易且更高效。
4、Fragments 间共享数据
Activity 中的两个或更多 Fragment 需要相互通信,可以通过 ViewModel 进行数据共享
数据恢复原理
问题:
- 在哪里、怎么保存数据?
- 在哪里、怎么恢复数据?
- 为什么在配置改变时ViewModel没有销毁,而在Activity finish时ViewModel销毁了?
源码分析入口:
1、应用启动、旋转屏幕
ComponentActivity.java
onRetainNonConfigurationInstance保存状态getLastNonConfigurationInstance获取之前保存的状态用于恢复。返回值: 之前onRetainNonConfigurationInstance 的返回值
2、
ViewModel 实例获取
viewModel = ViewModelProvider(this).get(NamedViewModel::class.java)ViewModelProvider(ViewModelStoreOwner owner)ViewModelProvider(ViewModelStore store, Factory factory)
参数1:getViewModelStore方法中从Activity的NonConfigurationInstances中取ViewModelStore,取不到就new一个
参数2:Factory中反射生成ViewModel实例ViewModelStoreOwner#getViewModelStore()
关系:
ViewModelProvider(owner)-->ViewModelStore-->ViewModel
ViewModelStore里面用map缓存ViewModelViewModelProvider对ViewModelStore进行一层封装,负责创建或提供ViewModelStoreViewModelStore属于ViewModelStoreOwner(Activity、Fragment..)
3、保存 ViewModel
保存在NonConfigurationInstances中
4、ViewModel 销毁
ViewModelStore#clear
问题解答:
- 在哪里、怎么保存数据?
配置改变时会调用
onRetainNonConfigurationInstance,将ViewModelStore放入NonConfigurationInstances实例中,该实例就作为onRetainNonConfigurationInstance方法的返回值。ViewModelStore中用map缓存了ViewModel。
- 在哪里、怎么恢复数据?
viewModel是在onCreate方法中去获取的,ViewModelProvider调用ViewModelStoreOwner#getViewModelStore去获取ViewModelStore。通过getLastNonConfigurationInstance获取到配置改变时保存的NonConfigurationInstances实例,进而获取到里面的ViewModelStore,ViewModelStore就缓存了ViewModel。
- 为什么在配置改变时
ViewModel没有销毁,而在Activityfinish时ViewModel销毁了?
ViewModelStoreOwner(如ComponentActivity)中生命周期注册了生命周期监听,只有在发生onDestroy事件并且是非配置改变引起时,才调用ViewModelStore#clear方法清除数据
总结:
Activity 在横竖屏切换时悄悄保存了ViewModelStore,放到了NonConfigurationInstances 实例里面,横竖屏切换时保存了又恢复了回来,相当于 ViewModel 实例就一直在,也就避免了横竖屏切换时的数据丢失。
onSaveInstanceState 和 onRetainNonConfigurationInstance 的区别
- 颗粒度不一样。
onSaveInstanceState()是保存到Bundle****中,只能保存Bundle****能接受的数据类型,比如一些基本类型的数据。而onRetainNonConfigurationInstance()可以保存任何类型的数据,数据类型是Object onSaveInstanceState()数据最终存储到ActivityManagerService的ActivityRecord中了,也就是存到系统进程中去了。而onRetainNonConfigurationInstance()数据是存储到ActivityClientRecord中,也就是存到应用本身的进程中了onSaveInstanceState存到系统进程中,所以App被杀之后还是能恢复的。而onRetainNonConfigurationInstance存到本身进程中,App被杀是没法恢复的。