ViewModel
处于数据逻辑层,他的生命周期贯穿整个宿主,如act因屏幕旋转销毁重建时,其依然存活,只有act.finish后,才会自动销毁,因此可以用他来维持宿主的数据状态。现在比较流行的方式是把他当做唯一数据源来驱动UI展示:
1 | view层: view (act / fragment) |
另外,还可以通过共享viewModel实现页面间通信,如两个fragment共享act的一个viewModel。
本文源码基于SDK 29
使用
引入依赖:
1 | def lifecycle_version = "2.2.0" |
创建ViewModel
,
1 | class CommonViewModel extends ViewModel { |
在布局文件中使用,
1 | <layout xmlns:android="http://schemas.android.com/apk/res/android" |
在act中使用,
1 | class ViewModelActivity extends BaseActivity { |
在onCreate方法打印ViewModel的hashCode,可见屏幕旋转导致act重建时,mCommonViewModel还是同一个实例,
原理
以ViewModelProviders.of(this).get(CommonViewModel.class)
为入口,先看ViewModelProviders.of
,
1 | //ViewModelProviders.java |
这里只需知道ViewModelProviders.of
得到了当前act的ViewModelProvider
,接着看get
方法,
1 | //ViewModelProvider.java |
看起来代码不是很多,那么viewModel是如何实现act重建后依然存活的呢?
首先viewModel存储在mViewModelStore,而这个store是创建ViewModelProvider时传进来的,即activity.getViewModelStore()
,
1 | //ComponentActivity.java |
那NonConfigurationInstances
又是如何存活的呢?
1 | //Activity.java |
mLastNonConfigurationInstances在attach
的时候获取值,
1 | //Activity.java |
Activity.attach
在ActivityThread.performLaunchActivity
中被调用,
1 | //ActivityThread.java |
可见来源于ActivityClientRecord.lastNonConfigurationInstances
,查找一下可知,r.lastNonConfigurationInstances
在performDestroyActivity
时赋值,
1 | //ActivityThread.java |
这边有点绕,再看到activity,
1 | //Activity.java |
至此,简单的概括一下就是,
Activity销毁时,借助
ActivityClientRcord
来间接保存viewModelStore
;Activity重建时,从ActivityClientRcord
中间接取出viewModelStore
。
而所有ActivityClientRcord
又被存储在ActivityThread
的成员变量里能长期存活:
1 | //ActivityThread.java |
至于act退出时viewModel可以自动销毁,是因为ComponentActivity
里添加了一个观察者:
1 | //ComponentActivity.java |
关于Lifecycles
,可以阅读我的早些的笔记。
优缺点
- 优点:
- 页面退出时,自动销毁
- 屏幕旋转、语言切换后数据不丢失,而onSaveInstanceState在面对复杂数据时需要序列化
- 不持有view层,方便单元测试
- 缺点:
- 虽然要比
onSaveInstanceState
简单,但是viewModel
只能在屏幕旋转和语言切换后的页面重建维持数据,当页面意外销毁时数据无法恢复,而这点onSaveInstanceState
可以做到,关于viewModel
如何实现这一点,可以看我的下一篇笔记。
- 虽然要比