生命周期组件三巨头 Lifecycle、 LiveData、 ViewModel,他们都是很好的搭档,基本都是组合使用。
1. 概述
1.1 历史
在 ViewModel 之前, Google就推出了 MVVM 架构,在2015年时,就为该架构提供了支持库 DataBinding
, 但是它的推行并不顺利,它起初排查问题较难,被许多开发者诟病。
虽然发展至今,已经迭代到一个很完善的版本,但是使用量依旧不是很多。之前看郭神的文章,他也是看衰这个库,认为以后还是使用 ViewModel
更好。
ViewModel
是2017年Google I/O大会推出组件,它的作用和名字一样,就是规范VM层。
1.2 ViewModel的作用
ViewModel 可以感知组件生命周期,所以它通过这个特性来存储和管理视图相关数据。它主要有以下几个特点:
- 当 Activity 被销毁时,我们可以使用
onSaveInstanceState()
方法恢复其数据, 这种方法仅适用于恢复少量的支持序列化、反序列化的数据,不适用于恢复大量数据、图表等数据 ,而 ViewModel 都可以支持 - 和 MVP 中的 P 层类似, ViewModel 可以有效分离 视图数据逻辑(M层) 和 视图控制器(V层), 避免V层代码臃肿
- V层如果进行网络请求,那么不能保证在数据返回时,页面还处于活跃状态,因为代码中写入大量的逻辑,来管理数据和避免内存泄漏。 而 ViewModel 恰恰可以避免内存泄漏
2. 使用
导入参照: Jetpack学习之 Lifecycle
我们来自定义一个ViewModel,如下,它继承了 ViewModel
抽象类,在获取里面 LiveData 实例时,会去做一个耗时操作然后更新这个数据:
class MyViewModel : ViewModel() {
private val str: MutableLiveData<String> by lazy {
MutableLiveData<String>().also {
requestStr()
}
}
fun getStr(): LiveData<String> {
return str
}
private fun requestStr() {
val executor = Executors.newSingleThreadExecutor()
executor.submit {
Thread.sleep(5000)
str.postValue("Rikka")
}
}
}
接着我们需要在 Activity 中使用它:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val model = ViewModelProvider(this).get(MyViewModel::class.java)
model.getStr().observe(this, Observer {
// 更新ui
Log.d(TAG, it)
})
}
获取 ViewModel 的方式有多种,除了上面代码中的 ViewModelProvider
, 还有 by viewModels()
等形式获取,需要相应的库来支持。
ViewModel 配合 LiveData 使用后,打印结果为:
这里需要注意:
- ViewModel 绝不能引用视图、Lifecycle 或可能存储对 Activity 上下文的引用的任何类。 因为 ViewModel 存在的时间会比这些特定的实例更长。 它可以包含 LiveData 对象,但
ViewModel
绝对不能去观察它的更改。
如果 ViewModel 需要 Application 上下文,可以扩展AndroidViewModel
用于接收 Application 的构造函数。 - 一个 Activity 上可以有多个 Fragment, 所以这些 Fragment 可以共享一个 ViewModel, 通过 ViewModelProvider 观察到两Fragment 都是处于同一个容器,那么就会为他们提供同一实例的 ViewModel
3. 原理
3.1 ViewModel 的生命周期
ViewModel 的生命周期是贯穿了整个 Activity 的生命周期
您通常在系统首次调用 Activity 对象的 onCreate()
方法时请求 ViewModel。系统可能会在 Activity 的整个生命周期内多次调用 onCreate(),如在旋转设备屏幕时。ViewModel 存在的时间范围是从首次请求 ViewModel 直到 Activity 完成并销毁。
3.2 ViewModel的创建
我们可以从 ViewModelProvider(ViewModelStoreOwner).get(Class<T>)
入手:
public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
: NewInstanceFactory.getInstance());
}
要求传入一个 ViewModelStroreOwner
, 而 ComponentActivity
/ Fragment
都实现了这个接口,这个接口只有一个函数 getViewModelStore()
, 所以实现的方法都会去创建一个 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());
}
public final void clear() {
for (ViewModel vm : mMap.values()) {
vm.clear();
}
mMap.clear();
}
}
这里面就是包含了一个 Map, k 是 String 也就是ViewModel 的名称, 而 value 就是 ViewModel 的实例。 再来看看 ViewModelProvider
的 get 方法:
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
// 1
ViewModel viewModel = mViewModelStore.get(key);
// 2
if (modelClass.isInstance(viewModel)) {
if (mFactory instanceof OnRequeryFactory) {
((OnRequeryFactory) mFactory).onRequery(viewModel);
}
return (T) viewModel;
} else {
if (viewModel != null) {
}
}:
if (mFactory instanceof KeyedFactory) {
viewModel = ((KeyedFactory) mFactory).create(key, modelClass);
} else {
// 3
viewModel = mFactory.create(modelClass);
}
// 4
mViewModelStore.put(key, viewModel);
return (T) viewModel;
}
注释1:从 ViewModelStore 的 Map 中去获取 ViewModel
注释2: 如果Map中获取到的 ViewModel 和 想要获取的类型一致,则返回这个实例
注释3、4:如果 Map中获取的为null,或者和想要获取的类型不一致,则通过 Factory.create()
创建一个实例,并将它传入到 Map 中。
这个 Factory 如果未指定, 一般就是 NewInstanceFactory
,来看看它的 create()
方法:
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
try {
return modelClass.newInstance();
} catch (InstantiationException e) {
throw new RuntimeException("Cannot create an instance of " + modelClass, e);
} catch (IllegalAccessException e) {
throw new RuntimeException("Cannot create an instance of " + modelClass, e);
}
}
源码就是通过反射的方式来创建实例。
至于Map中的ViewModel名称,是这样规范的:DEFAULT_KEY + “:” + canonicalName, 即
androidx.lifecycle.ViewModelProvider.DefaultKey:XXX.XXX.XXViewModel
3.3 类图
参考文章
《Android进阶指北》第10章 ViewModel
深入了解架构组件之ViewModel
Android中文网