JetPack——ViewModel简介&原理

639 阅读4分钟

简介

ViewModel 类旨在以注重生命周期的方式存储和管理界面相关的数据,让数据可在发生屏幕旋转等配置更改后继续留存。

ViewModel有两个特性。

  1. 当配置发生改变时(例如:旋转屏幕),重新创建的Activity能够通过ViewModel将数据还原回来;
  2. 当按返回键或者调用finish方法时,ViewModel能够感知到onDestroy事件,同时将ViewModel保存的 Closeable 对象关闭掉(例如:主动关闭协程

当屏幕旋转时,会调用ActivityonRetainNonConfigurationInstance方法。ViewModel组件正是通过该方法将ViewModel保存起来,给重建的Activity使用。

1、数据保持与恢复

对于简单的数据,Activity 可以使用 onSaveInstanceState() 方法从 onCreate() 中的捆绑包恢复其数据,但此方法仅适合可以序列化再反序列化的少量数据,而不适合数量可能较大的数据,如用户列表或位图。

ViewModel 生命周期:

ViewModel生命周期.png

ViewModel 对象存在的时间范围是获取 ViewModel 时传递给 ViewModelProvider 的 Lifecycle

ViewModel 将一直留在内存中,直到限定其存在时间范围的 Lifecycle 永久消失:对于 activity,是在 activity 完成时;而对于 fragment,是在 fragment 分离时。

注意ViewModel 不能引用视图、Lifecycle 或可能存储对 Activity 上下文的引用的任何类。

2、异步回调问题

界面控制器经常需要进行可能需要一些时间才能返回的异步调用。界面控制器需要管理这些调用,并确保系统在其销毁后清理这些调用以避免潜在的内存泄漏。此项管理需要大量的维护工作,并且在为配置更改重新创建对象的情况下,会造成资源的浪费,因为对象可能需要重新发出已经发出过的调用。

3、分担 UI controller 负担

诸如 ActivityFragment 之类的界面控制器主要用于显示界面数据、对用户操作做出响应或处理操作系统通信(如权限请求)。如果要求界面控制器也负责从数据库或网络加载数据,那么会使类越发膨胀。为界面控制器分配过多的责任可能会导致单个类尝试自己处理应用的所有工作,而不是将工作委托给其他类。以这种方式为界面控制器分配过多的责任也会大大增加测试的难度。

从界面控制器逻辑中分离出视图数据所有权的操作更容易且更高效。

4、Fragments 间共享数据

Activity 中的两个或更多 Fragment 需要相互通信,可以通过 ViewModel 进行数据共享

数据恢复原理

问题:

  1. 在哪里、怎么保存数据?
  2. 在哪里、怎么恢复数据?
  3. 为什么在配置改变时ViewModel没有销毁,而在Activity finish时ViewModel销毁了?

源码分析入口:

1、应用启动、旋转屏幕

ComponentActivity.java

  • onRetainNonConfigurationInstance 保存状态
  • getLastNonConfigurationInstance 获取之前保存的状态用于恢复。返回值: 之前onRetainNonConfigurationInstance 的返回值

旋转屏幕.png 2、ViewModel 实例获取

  • viewModel = ViewModelProvider(this).get(NamedViewModel::class.java)
  • ViewModelProvider(ViewModelStoreOwner owner)
  • ViewModelProvider(ViewModelStore store, Factory factory)

参数1:getViewModelStore方法中从ActivityNonConfigurationInstances中取ViewModelStore,取不到就new一个

参数2:Factory中反射生成ViewModel实例ViewModelStoreOwner#getViewModelStore()

关系:

ViewModelProvider(owner)-->ViewModelStore-->ViewModel

  1. ViewModelStore 里面用map缓存ViewModel
  2. ViewModelProviderViewModelStore 进行一层封装,负责创建或提供 ViewModelStore
  3. ViewModelStore 属于 ViewModelStoreOwner(ActivityFragment..)

3、保存 ViewModel

保存在NonConfigurationInstances

4、ViewModel 销毁

ViewModelStore#clear

问题解答:

  1. 在哪里、怎么保存数据?

配置改变时会调用onRetainNonConfigurationInstance ,将 ViewModelStore 放入 NonConfigurationInstances 实例中,该实例就作为 onRetainNonConfigurationInstance 方法的返回值。ViewModelStore 中用 map 缓存了 ViewModel

  1. 在哪里、怎么恢复数据?

viewModel 是在 onCreate 方法中去获取的,ViewModelProvider 调用 ViewModelStoreOwner#getViewModelStore 去获取 ViewModelStore。通过getLastNonConfigurationInstance 获取到配置改变时保存的NonConfigurationInstances 实例,进而获取到里面的 ViewModelStoreViewModelStore 就缓存了 ViewModel

  1. 为什么在配置改变时 ViewModel 没有销毁,而在 Activity finishViewModel 销毁了?

ViewModelStoreOwner(如 ComponentActivity)中生命周期注册了生命周期监听,只有在发生onDestroy 事件并且是非配置改变引起时,才调用 ViewModelStore#clear 方法清除数据

总结:

Activity 在横竖屏切换时悄悄保存了ViewModelStore,放到了NonConfigurationInstances 实例里面,横竖屏切换时保存了又恢复了回来,相当于 ViewModel 实例就一直在,也就避免了横竖屏切换时的数据丢失。

onSaveInstanceState 和 onRetainNonConfigurationInstance 的区别

  1. 颗粒度不一样。onSaveInstanceState()是保存到 Bundle ****中,只能保存 Bundle ****能接受的数据类型,比如一些基本类型的数据。而onRetainNonConfigurationInstance() 可以保存任何类型的数据,数据类型是Object
  2. onSaveInstanceState()数据最终存储到 ActivityManagerServiceActivityRecord 中了,也就是存到系统进程中去了。而onRetainNonConfigurationInstance() 数据是存储到ActivityClientRecord中,也就是存到应用本身的进程中了
  3. onSaveInstanceState存到系统进程中,所以App被杀之后还是能恢复的。而onRetainNonConfigurationInstance存到本身进程中,App被杀是没法恢复的。