0
点赞
收藏
分享

微信扫一扫

Jetpack学习之 ViewModel


生命周期组件三巨头 Lifecycle、 LiveData、 ViewModel,他们都是很好的搭档,基本都是组合使用。

目录

  • ​​1. 概述​​
  • ​​1.1 历史​​
  • ​​1.2 ViewModel的作用​​
  • ​​2. 使用​​
  • ​​3. 原理​​
  • ​​3.1 ViewModel 的生命周期​​
  • ​​3.2 ViewModel的创建​​
  • ​​3.3 类图​​
  • ​​参考文章​​

1. 概述

1.1 历史

在 ViewModel 之前, Google就推出了 MVVM 架构,在2015年时,就为该架构提供了支持库 ​​DataBinding​​​, 但是它的推行并不顺利,它起初排查问题较难,被许多开发者诟病。
虽然发展至今,已经迭代到一个很完善的版本,但是使用量依旧不是很多。之前看郭神的文章,他也是看衰这个库,认为以后还是使用 ​​​ViewModel​​ 更好。

​ViewModel​​ 是2017年Google I/O大会推出组件,它的作用和名字一样,就是规范VM层

1.2 ViewModel的作用

ViewModel 可以感知组件生命周期,所以它通过这个特性来存储和管理视图相关数据。它主要有以下几个特点:

  1. 当 Activity 被销毁时,我们可以使用​​onSaveInstanceState()​​ 方法恢复其数据, 这种方法仅适用于恢复少量的支持序列化、反序列化的数据,不适用于恢复大量数据、图表等数据,而 ViewModel 都可以支持
  2. 和 MVP 中的 P 层类似, ViewModel 可以有效分离 视图数据逻辑(M层) 和 视图控制器(V层), 避免V层代码臃肿
  3. 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 使用后,打印结果为:

Jetpack学习之 ViewModel_ide


这里需要注意:

  1. ViewModel 绝不能引用视图、Lifecycle 或可能存储对 Activity 上下文的引用的任何类。因为 ViewModel 存在的时间会比这些特定的实例更长。 它可以包含 LiveData 对象,但​​ViewModel​​​ 绝对不能去观察它的更改。
    如果 ViewModel 需要 Application 上下文,可以扩展​​​AndroidViewModel​​ 用于接收 Application 的构造函数。
  2. 一个 Activity 上可以有多个 Fragment, 所以这些 Fragment 可以共享一个 ViewModel, 通过 ViewModelProvider 观察到两Fragment 都是处于同一个容器,那么就会为他们提供同一实例的 ViewModel

3. 原理

3.1 ViewModel 的生命周期

ViewModel 的生命周期是贯穿了整个 Activity 的生命周期

Jetpack学习之 ViewModel_生命周期_02


您通常在系统首次调用 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 类图

Jetpack学习之 ViewModel_数据_03

参考文章

《Android进阶指北》第10章 ViewModel
​​​深入了解架构组件之ViewModel​​​​Android中文网​​


举报

相关推荐

0 条评论