• 作者:老汪软件技巧
  • 发表时间:2024-09-01 11:02
  • 浏览量:

ViewModel的实例化方法有很多,例如在Activity里可以使用ViewModelProvider实例化一个对象:

private val vm:ViewModel by lazy {
    ViewModelProvider(this)[ViewModel::class.java]
}

也可以更简单一点用委托属性:

private val vm :ViewModel by viewModels()

在Compose里还可以这样:

val model: ViewModel = viewModel()

对于复杂一点需要提供Factory的情况:

val model: ViewModel = viewModel(
    factory = object : ViewModelProvider.Factory{
        override fun  create(modelClass: Class<T>): T {
            if (modelClass.isAssignableFrom(ViewModel::class.java)) {
                return ViewModel() as T
            }
            throw IllegalArgumentException("Unknown ViewModel class")
        }
    }
)

那么对于上述情况,ViewModel是如何具体实例化和跟随Activity的生命周期的呢?

一、相关类介绍1、基本类类名介绍

ViewModel

抽象类,调用clear方法销毁资源,销毁前回调onCleared方法

ViewModelStore

利用map存储ViewModel对象,clear方法:遍历存储的ViewModel对象调用其clear方法

ViewModelStoreOwner

接口,需要实例化一个ViewModelStore对象

ViewModelProvider

生成ViewModel对象的中间人

Factory

需实现create方法实例化一个ViewModel对象

HasDefaultViewModelProviderFactory

接口,需要实例化一个Factory对象

2、衍生类类名介绍

ComponentActivity

实现了ViewModelStoreOwner、HasDefaultViewModelProviderFactory接口

AndroidViewModel

ViewModel子类,包含Application参数

NewInstanceFactory

Factory的实现类,create方法通过无参构造函数实例化一个ViewModel对象,在它的伴生对象里有个属性instance,是NewInstanceFactory的对象,可以通过类名调用

AndroidViewModelFactory

NewInstanceFactory的子类,需要Application来实例化一个AndroidViewModel对象

SavedStateViewModelFactory

Factory的实现类,内部又维护一个Factory对象factory

3、Compose相关类名介绍

NavBackStackEntry

类似于ComponentActivity,实现了ViewModelStoreOwner、HasDefaultViewModelProviderFactory接口,在NavHost中,NavBackStackEntry对象与页面一一对应,一个页面对应一个NavBackStackEntry对象,并且一个页面拥有唯一id

NavViewModelStoreProvider

接口,需要实现getViewModelStore方法,根据id返回一个ViewModelStore实例

NavControllerViewModel

NavViewModelStoreProvider的实现类,同时继承ViewModel,维护一个map保存ViewModelStore,根据id从map中得到ViewModelStore实例,如果不存在就实例化一个

NavController

维护返回栈和viewModel等相关参数,返回栈:栈内为NavBackStackEntry实例,viewModel:NavControllerViewModel实例

二、ViewModelStore和Factory来源1、ViewModelStore来源

首先,ComponentActivity实现了ViewModelStoreOwner接口,因此它需要提供一个ViewModelStore

对于mViewModelStore

直接实例了一个ViewModelStore对象

2、Factory的来源

首先,ComponentActivity实现了HasDefaultViewModelProviderFactory接口,因此它需要提供一个Factory:

可以看到,提供的Factory是SavedStateViewModelFactory对象,在它的构造函数里,对它维护的Factory对象factory进行初始化

如果传入了application,就用getInstance方法,利用AndroidViewModelFactory的有参构造实例化,否则就用无参构造,反正都是AndroidViewModelFactory对象

所以到这里我们就知道了,SavedStateViewModelFactory想要实例化一个ViewModel有三个选择

外部不管那么多,具体的选择肯定在它的create方法里,来看相关代码:

findMatchingConstructor方法是查找是否有包含特定参数的构造方法,所以上面代码可以概况为:

如果ViewModel没有特定参数的构造方法,使用factory来实例化,否则就调用相关的构造方法传入相关参数来实例化。

由于factory是AndroidViewModelFactor的对象,因此再来看AndroidViewModelFactor的create方法

它的操作就是,如果包含application,就实例化为AndroidViewModel的对象,否则就调用父类的create方法,它的父类是NewInstanceFactory,NewInstanceFactor的create方法是通过反射调用无参构造实例化的。

总的概况就是:SavedStateViewModelFactory负责实例化特定参数的ViewModel,否则交给AndroidViewModelFactory,而AndroidViewModelFactory负责实例化AndroidViewModel对象,否则交给NewInstanceFactory,NewInstanceFactory通过无参构造实例化。

三、各情形下的实例化1、通过ViewModelProvider

private val vm:ViewModel by lazy {
    ViewModelProvider(this)[ViewModel::class.java]
}

短短的代码包含了两个过程

①实例化ViewModelProvider

它的构造方法是这样的

ComponentActivity实现了ViewModelStoreOwner接口,所以可以传入this,它也实现了HasDefaultViewModelProviderFactory接口,所以可以拿到Factory实例,调用另一个构造函数

销毁当前activity_在销毁阶段执行destroy_

②调用get方法

首先检查是不是本地类和匿名类,它们是不能创建ViewModel对象的,如果不是调用重载方法

store就是构造函数里的那个,它用map存储着ViewModel对象,所以上述代码可以概况为:

如果store里有就返回,否则就新建一个并存入store里

2、通过委托属性by viewModels()

private val vm :ViewModel by viewModels()

可以看到viewModels()其实是ComponentActivity的扩展方法,factory依旧是那个factory,viewModelStore依旧是那个viewModelStore,只是因为这是ComponentActivity的扩展方法,所以拿的更轻松一点。

继续看ViewModelLazy

所以它其实就是实现了对ViewModel的懒加载,其他都一样。

4、在Compose中使用

val model: ViewModel = viewModel()

这里面涉及内容很多,逐步分析

①非NavHost里获取ViewModelStore、Factory

在ComponentActivity里可以看到这一段代码

进入其中的一个set方法

发现它把ViewModelStoreOwner存到了根View里

然后再看Compose的LocalViewModelStoreOwner.current

发现如果LocalViewModelStoreOwner.current有值就返回,否则就把保存在view里的ViewModelStoreOwner拿出来,有了ViewModelStoreOwner调用其方法就可以得到ViewModelStore

既然ViewModelStore来自ComponentActivity,那么还可以得到ComponentActivity的Factory,这里不再赘述。

②NavHost里获取ViewModelStore、Factory

我们知道,使用NavHost需要提供NavController对象,在NavHost里,调用了NavController的setViewModelStore方法

在这里,如果没有嵌套NavHost的话,代码第一行得到的viewModelStoreOwner的值就是上一节提到的保存在View中的ViewModelStoreOwner对象。

深入看setViewModelStore方法

可以知道NavController维护了一个ViewModel实例,并且它是NavControllerViewModel.getInstance()方法得到的实例,继续深入

原来它是NavControllerViewModel对象,NavControllerViewModel在前面介绍过,它是NavViewModelStoreProvider接口的实现类,同时继承ViewModel,维护一个map保存ViewModelStore对象,所以在NavHost里实例化和存储ViewModelStore都在这里面

它实现的getViewModelStore方法就是,根据id从map中得到ViewModelStore实例,如果不存在就实例化一个

目前知道了ViewModelStore的创建和存储的地方,那么如何获取的呢?

前面说过,NavHost里的一个页面对应一个NavBackStackEntry实例,一个NavBackStackEntry又对应一个id,NavBackStackEntry的实例是在NavController里创建的,来看的NavBackStackEntry的create方法

重点是NavViewModelStoreProvider参数,NavController在调用这个方法时会传入它维护的NavControllerViewModel实例viewModel(NavViewModelStoreProvider是接口,NavControllerViewModel实现了它)

由于NavViewModelStoreProvider实现了ViewModelStoreOwner、HasDefaultViewModelProviderFactory接口,所以他需要和ComponentAvtivity一样提供Factory和ViewModelStore

其中viewModelStoreProvider就是构造函数里传进来的NavViewModelStoreProvider对象,调用它的方法传入唯一id,返回一个ViewModelStore对象,它具体的实现上面分析过。

再看Factory

factory还是一样的用的SavedStateViewModelFactory,直接懒加载实例化一个。

总结就是:首先利用NavHost外部的ViewModelStore创建存储一个NavControllerViewModel实例,这个实例的作用就是用map存储ViewModelStore对象,这个map的key就是每个页面的id,由此实现了一个页面一个ViewModelStore。如果没有嵌套NavHost,外部的ViewModelStore就是ComponentActivity的ViewModelStore,否则就是发生嵌套的那个页面的ViewModelStore

有了ViewModelStore再来具体谈谈实例化ViewModel

③实例ViewModel

在NavHost外面,

LocalViewModelStoreOwner.current前面分析过,NavHost外面,拿的是存储的View里的,如果是NavHost里,每个页面的ViewModelStoreOwner都不同,那么如何绑定页面,在不同页面获取到不同的ViewModelStoreOwner的呢?

在NavHost可以看到这样的代码

点进去

原来这是NavBackStackEntry的扩展方法,NavHost会将当前页面的ViewModelStoreOwner赋值给LocalViewModelStoreOwner.current,所以在不同页面获得的ViewModelStoreOwner不同。

四、生命周期管理

ComponentActivity重写了onRetainNonConfigurationInstance()方法,这个方法当Activity由于配置更改而被销毁并重新创建时被调用,可以将需要存储的数据返回。

而getLastNonConfigurationInstance()是获取存储的数据

mViewModelStore是ComponentActivity维护的ViewModelStore对象,可以看到,如果它为null,就拿上次保存的数据并复制然后进行返回存储。

ComponentActivity还监测了生命周期

当不是因配置发生变化而引起Destory时,调用ViewModelStore的clear方法。

ViewModelStore的clear方法遍历存储的VieModel,调用其clear方法。

在Compose里依旧如此,各NavHost页面的ViewModelStore存储在NavControllerViewModel里,而NavControllerViewModel存储在ComponentActivity的ViewModelStore里,因此都处在一条链上,都能够与Activity的生命周期保持联动。