CreationExtras|CreationExtras 来了,创建 ViewModel 的新方式
Androidx-Lifecycle 在近期迈入到了 2.5.0 版本,其中最重要的一个变化是引入了 CreatioinExtras
的概念。一句话概括 CreationExtras
的作用:帮助我们在创建 ViewModel
时更优雅地获取初始化参数
1. 现状的问题
先回顾一下目前为止的 ViewModel 的创建方式
val vm : MyViewModel by viewModels()
我们知道其内部其实是通过
ViewModelProvider
获取 VM。当 VM 不存在时使用 ViewModelProvider.Factory
创建 VM 实例。默认 Factory 使用反射创建实例,所以 VM 的构造函数不能有参数 。如果希望使用初始化参数创建 VM 则需要定义自己的 Factory :class MyViewModelFactory(
private val application: Application,
private val param: String
) : ViewModelProvider.Factory {override fun create(modelClass: Class): T {
return MyViewModel(application, param) as T
}
}
【CreationExtras|CreationExtras 来了,创建 ViewModel 的新方式】然后,在 Activity 或 Fragment 中声明 VM 的现场,创建自定义 Factory:
val vm : MyViewModel by viewModels {
MyViewModelFactory(application, "some data")
}
"some data"
可能来自 Activity 的 Intent
或者 Fragment 的 argements
,所以一个真实项目中为了准备 VM 参数的代码可能要复杂得多。一个持有“状态”的 Factory 不利于复用,为了保证 VM 创建时的正确性,往往需要为每个 VM 都配备专属的 Factory,失去了“工厂”原本存在的意义。随着 App 的页面越发复杂,每一处需要共享 VM 的地方都要单独构建 Factory ,冗余代码也越来越多。除了直接使用
ViewModelProvider.Factory
,还有其他几种初始化方式,例如借助 SavedStateHandler
等,但是无论何种方式本质上都是借助了 ViewModelProvider.Factory
,都免不了上述 Stateful Factory 的问题。2. CretionExtras 是怎么解决的? Lifecycle 2.5.0-alpha01 开始引入了
CreationExtras
的概念,它替代了 Factory 的任务为 VM 初始化所需的参数,Factory 无需再持有状态。我们知道
ViewModelProvider.Factory
使用 create(modelClass)
创建 VM ,在 2.5.0 之后方法签名发生了如下变化://before 2.5.0
fun create(modelClass: Class): T
//after 2.5.0
fun create(modelClass: Class, extras: CreationExtras): T
2.5.0 之后在创建 VM 时可以通过
extras
获取所需的初始化参数。定义 Factory 变成下面这样:class ViewModelFactory : ViewModelProvider.Factory {
override fun create(modelClass: Class, extras: CreationExtras): T {
return when (modelClass) {
MyViewModel::class.java -> {
// 通过extras获取自定义参数
val params = extras[extraKey]!!
// 通过extras获取application
val application = extras[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY]
// 创建 VM
MyViewModel(application, params)
}
// ...
else -> throw IllegalArgumentException("Unknown class $modelClass")
} as T
}
}
一个 Stateless 的 Factory 可以更好地复用。我们可以在一个 Factory 中使用
when
处理所有类型的 VM 创建,一次定义多处使用。3. CreationExtras.Key 上面代码中使用
extras[key]
获取初始化参数,key
的类型是 CreationExtras.Key
。看一下
CreationExtras
的定义就明白了,成员 map
后文会介绍到public sealed class CreationExtras {
internal val map: MutableMap, Any?> = mutableMapOf()/**
* Key for the elements of [CreationExtras]. [T] is a type of an element with this key.
*/
public interface Key/**
* Returns an element associated with the given [key]
*/
public abstract operator fun get(key: Key): T?/**
* Empty [CreationExtras]
*/
object Empty : CreationExtras() {
override fun get(key: Key): T? = null
}
}
Key
的泛型 T
代表对应 Value
的类型。相对于 Map
,这种定义方式可以更加类型安全地获取多种类型的键值对,CoroutineContext
等也是采用这种设计。如下, 我们可以自定义一个
String
类型数据的 Key
private val extraKey = object : CreationExtras.Key {}
系统以及提供了几个预置的 Key 供使用:
CreationExtras.Key | Descriptions |
---|---|
ViewModelProvider.NewInstanceFactory.VIEW\_MODEL\_KEY | ViewModelProvider 可以基于 key 区分多个 VM 实例,VIEW\_MODEL\_KEY 用来提供当前 VM 的这个 key |
ViewModelProvider.AndroidViewModelFactory.APPLICATION\_KEY | 提供当前 Application context |
SavedStateHandleSupport.SAVED\_STATE\_REGISTRY\_OWNER\_KEY | 提供创建 createSavedStateHandle 所需的 SavedStateRegistryOwner |
SavedStateHandleSupport.VIEW\_MODEL\_STORE\_OWNER\_KEY | createSavedStateHandle 所需的 ViewModelStoreOwner |
SavedStateHandleSupport.DEFAULT\_ARGS\_KEY | createSavedStateHandle 所需的 Bundle |
Key
都跟 SavedStateHandle
的创建有关,后文会进行介绍4. 如何创建 CreationExtras 那么我们如何创建
Extras
并传入 create(modelClass, extras)
的参数中呢?从
CreatioinExtras
的定义中我们知道它是一个密封类,因此无法直接实例化。我们需要使用其子类 MutableCreationExtras
来创建实例,这是一种读写分离的设计思想,保证了使用处的不可变性。顺便看一眼
MutableCreationExtras
的实现吧,非常简单:public class MutableCreationExtras(initialExtras: CreationExtras = Empty) : CreationExtras() {init {
map.putAll(initialExtras.map)
}
/**
* Associates the given [key] with [t]
*/
public operator fun set(key: Key, t: T) {
map[key] = t
}public override fun get(key: Key): T? {
@Suppress("UNCHECKED_CAST")
return map[key] as T?
}
}
还记得
CreationExtras
中的 map
成员吗,这里使用到了。从 initialExtras
的使用可看出来 CreationExtras
可以通 merge 实现内容的继承,例如:val extras = MutableCreationExtras().apply {
set(key1, 123)
}
val mergedExtras = MutableCreationExtras(extras).apply {
set(key2, "test")
}mergedExtras[key1] // => 123
mergedExtras[key2] // => test
ViewModelProvider
的 defaultCreationExtras
也是通过 merge 实现的传递。看一下获取 VM 的代码:public open operator fun get(key: String, modelClass: Class): T {
val viewModel = store[key]val extras = MutableCreationExtras(defaultCreationExtras)
extras[VIEW_MODEL_KEY] = keyreturn factory.create(
modelClass,
extras
).also { store.put(key, it) }
}
可以发现
extras
默认会继承一个 defaultCreationExtras
5. 默认参数 DefaultCreationExtras 上面提到的
defaultCreationExtras
实际上是 ViewModelProvider
从当前 Activity 或者 Fragment 中获取的。以 Activity 为例,我们可以通过重写
getDefaultViewModelCreationExtras()
方法,来提供 defaultCreationExtras
给 ViewModelProvider
,最终传入 create(modelClass, extras)
的参数注意: Activity 1.5.0-alpha01 和 Fragment 1.5.0-alpha01 之后才能重写 getDefaultViewModelCreationExtras 方法。之前的版本中,访问 defaultCreationExtras 将返回 CreationExtras.Empty看一下
ComponentActivity
的默认实现:public CreationExtras getDefaultViewModelCreationExtras() {
MutableCreationExtras extras = new MutableCreationExtras();
if (getApplication() != null) {
extras.set(ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY, getApplication());
}
extras.set(SavedStateHandleSupport.SAVED_STATE_REGISTRY_OWNER_KEY, this);
extras.set(SavedStateHandleSupport.VIEW_MODEL_STORE_OWNER_KEY, this);
if (getIntent() != null && getIntent().getExtras() != null) {
extras.set(SavedStateHandleSupport.DEFAULT_ARGS_KEY, getIntent().getExtras());
}
return extras;
}
有 Application 以及 Intent 等,前面介绍的预设 Key 都是这里注入的。
当我们需要使用 Activity 的 Intent 初始化 VM 时,代码如下:
object : ViewModelProvider.Factory {
override fun create(
modelClass: Class,
extras: CreationExtras
): T {
// 使用 DEFAULT_ARGS_KEY 获取 Intent 中的 Bundle
val bundle = extras[DEFAULT_ARGS_KEY]
val id = bundle?.getInt("id") ?: 0
return MyViewModel(id) as T
}
}
6. 对 AndroidViewModel 和 SavedStateHandle 的支持 前面说了,
CreationExtras
本质上就是让 Factory 变得无状态。以前为了构建不同参数类型的 ViewModel 而存在的各种特殊的 Factory 子类,比如 AndroidViewModel
的 AndroidViewModelFactory
以及 SavedStateHandler ViewModel
的 SavedStateViewModelFactory
等等,都会由于 CreationExtras
出现而逐渐退出舞台。class CustomFactory : ViewModelProvider.Factory {
override fun create(modelClass: Class, extras: CreationExtras): T {
return when (modelClass) {
HomeViewModel::class -> {
// Get the Application object from extras
val application = checkNotNull(extras[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY])
// Pass it directly to HomeViewModel
HomeViewModel(application)
}
DetailViewModel::class -> {
// Create a SavedStateHandle for this ViewModel from extras
val savedStateHandle = extras.createSavedStateHandle()
DetailViewModel(savedStateHandle)
}
else -> throw IllegalArgumentException("Unknown class $modelClass")
} as T
}
}
如上,无论
Application
还是 SavedStateHandler
都可以统一从 CreationExtras
获取。createSavedStateHandle()
扩展函数可以基于 CreationExtras
创建 SavedStateHandler
public fun CreationExtras.createSavedStateHandle(): SavedStateHandle {
val savedStateRegistryOwner = this[SAVED_STATE_REGISTRY_OWNER_KEY]
val viewModelStateRegistryOwner = this[VIEW_MODEL_STORE_OWNER_KEY]
val defaultArgs = this[DEFAULT_ARGS_KEY]
val key = this[VIEW_MODEL_KEY]
return createSavedStateHandle(
savedStateRegistryOwner, viewModelStateRegistryOwner, key, defaultArgs
)
}
所需的
savedStateRegistryOwner
等参数也来自 CreationExtras
,此外,查看 SavedStateViewModelFactory
的最新代码可知,其内部实现也像上面那样基于 CreationExtras
重构过了。7. 对 Compose 的支持 再来简单看看 Compose 中如何使用
CreationExtras
。注意 Gradle 依赖升级如下:
- androidx.activity:activity-compose:1.5.0-alpha01
- androidx.lifecycle:lifecycle-viewmodel-compose:2.5.0-alpha01
val owner = LocalViewModelStoreOwner.current
val defaultExtras =
(owner as? HasDefaultViewModelProviderFactory)?.defaultViewModelCreationExtras
?: CreationExtras.Emptyval extras = MutableCreationExtras(defaultExtras).apply {
set(extraKeyId, 123)
}val factory = remember {
object : ViewModelProvider.Factory {
override fun create(
modelClass: Class,
extras: CreationExtras
): T {
val id = extras[extraKeyId]!!
return MainViewModel(id) as T
}
}
}val viewModel = factory.create(MainViewModel::class.java, extras)
可以通过
LocalViewModelStoreOwner
获取当前的 defaultExtras
,然后根据需要添加自己的 extras
即可。8. 使用 DSL 创建 ViewModelFactory 2.5.0-alpha03 新增了用 DSL 创建 ViewModelFactory 的方式,
注意 Gradle 依赖升级如下:使用效果如下:
- androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.0-alpha03
- androidx.fragment:fragment-ktx:1.5.0-alpha03
val factory = viewModelFactory {
initializer {
TestViewModel(this[key])
}
}
viewModelFactory{...}
与 intializer{...}
的定义分别如下:public inline fun viewModelFactory(
builder: InitializerViewModelFactoryBuilder.() -> Unit
): ViewModelProvider.Factory =
InitializerViewModelFactoryBuilder().apply(builder).build()inline fun InitializerViewModelFactoryBuilder.initializer(
noinline initializer: CreationExtras.() -> VM
) {
addInitializer(VM::class, initializer)
}
InitializerViewModelFactorBuilder
用来 build 一个 InitializerViewModelFactory
,稍后对其进行介绍。addInitializer
将 VM::class
与对应的 CreationExtras.() -> VM
存入 initializers
列表:private val initializers = mutableListOf>()fun addInitializer(clazz: KClass, initializer: CreationExtras.() -> T) {
initializers.add(ViewModelInitializer(clazz.java, initializer))
}
刚提到的
InitializerViewModelFactor
在 create
时,通过 initializers
创建 VM,代码如下:class InitializerViewModelFactory(
private vararg val initializers: ViewModelInitializer<*>
) : ViewModelProvider.Factory {override fun create(modelClass: Class, extras: CreationExtras): T {
var viewModel: T? = null
@Suppress("UNCHECKED_CAST")
initializers.forEach {
if (it.clazz == modelClass) {
viewModel = it.initializer.invoke(extras) as? T
}
}
return viewModel ?: throw IllegalArgumentException(
"No initializer set for given class ${modelClass.name}"
)
}
}
由于
initializers
是一个列表,所以可以存储多个 VM 的创建信息,因此可以通过 DSL 配置多个 VM 的创建:val factory = viewModelFactory {
initializer {
MyViewModel(123)
}
initializer {
MyViewModel2("Test")
}
}
推荐阅读
- Docker|Docker 笔记 - 使用 docker 镜像快速创建一个 mysql 实例
- 详解在Python中创建条形图追赶动画
- Day 33/100 JavaScript 创建对象的四种方式
- 第一阶段|【第一阶段 day22 面向对象】面向过程 面向对象 类 对象 类与对象的关系 对象创建过程分析 封装 访问控制符
- 第五篇-|第五篇- 抖音的强大对手来了,用Flutter手撸一个抖音国际版,看看有多炫
- Linux|Linux环境搭建,创建用户和删除用户
- c/c++|函数栈帧的创建和销毁(程序员必了解内容)
- JAVA人生|春招来了,这波神仙打架你要不要参战
- Spring|ssm框架整合
- flutter学习|flutter+Dart类的创建(六)