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 <T : ViewModel> create(modelClass: Class<T>): T {return MyViewModel(application, param) as T
}
}
而后,在 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 <T : ViewModel> create(modelClass: Class<T>): T
//after 2.5.0
fun <T : ViewModel> create(modelClass: Class<T>, extras: CreationExtras): T
2.5.0 之后在创立 VM 时能够通过 extras
获取所需的初始化参数。定义 Factory 变成上面这样:
class ViewModelFactory : ViewModelProvider.Factory {override fun <T : ViewModel> create(modelClass: Class<T>, 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<Key<*>, Any?> = mutableMapOf()
/**
* Key for the elements of [CreationExtras]. [T] is a type of an element with this key.
*/
public interface Key<T>
/**
* Returns an element associated with the given [key]
*/
public abstract operator fun <T> get(key: Key<T>): T?
/**
* Empty [CreationExtras]
*/
object Empty : CreationExtras() {override fun <T> get(key: Key<T>): T? = null
}
}
Key
的泛型 T
代表对应 Value
的类型。绝对于 Map<K,V>
,这种定义形式能够更加类型平安地获取多种类型的键值对,CoroutineContext
等也是采纳这种设计。
如下,咱们能够自定义一个 String
类型数据的 Key
private val extraKey = object : CreationExtras.Key<String> {}
零碎以及提供了几个预置的 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 <T> set(key: Key<T>, t: T) {map[key] = t
}
public override fun <T> get(key: Key<T>): 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 <T : ViewModel> get(key: String, modelClass: Class<T>): T {val viewModel = store[key]
val extras = MutableCreationExtras(defaultCreationExtras)
extras[VIEW_MODEL_KEY] = key
return 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 <T : ViewModel> create(
modelClass: Class<T>,
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 <T : ViewModel> create(modelClass: Class<T>, 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.Empty
val extras = MutableCreationExtras(defaultExtras).apply {set(extraKeyId, 123)
}
val factory = remember {
object : ViewModelProvider.Factory {
override fun <T : ViewModel> create(
modelClass: Class<T>,
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 <reified VM : ViewModel> InitializerViewModelFactoryBuilder.initializer(noinline initializer: CreationExtras.() -> VM
) {addInitializer(VM::class, initializer)
}
InitializerViewModelFactorBuilder
用来 build 一个 InitializerViewModelFactory
,稍后对其进行介绍。
addInitializer
将 VM::class
与对应的 CreationExtras.() -> VM
存入 initializers
列表:
private val initializers = mutableListOf<ViewModelInitializer<*>>()
fun <T : ViewModel> addInitializer(clazz: KClass<T>, initializer: CreationExtras.() -> T) {initializers.add(ViewModelInitializer(clazz.java, initializer))
}
刚提到的 InitializerViewModelFactor
在 create
时,通过 initializers
创立 VM,代码如下:
class InitializerViewModelFactory(private vararg val initializers: ViewModelInitializer<*>) : ViewModelProvider.Factory {override fun <T : ViewModel> create(modelClass: Class<T>, 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")
}
}