关于android:CreationExtras-来了创建-ViewModel-的新方式

41次阅读

共计 9123 个字符,预计需要花费 23 分钟才能阅读完成。

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

ViewModelProviderdefaultCreationExtras 也是通过 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() 办法,来提供 defaultCreationExtrasViewModelProvider,最终传入 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 子类,比方 AndroidViewModelAndroidViewModelFactory 以及 SavedStateHandler ViewModelSavedStateViewModelFactory 等等,都会因为 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,稍后对其进行介绍。

addInitializerVM::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))
}

刚提到的 InitializerViewModelFactorcreate 时,通过 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")
    }
}

正文完
 0