一、概念
1.1 恢复页面状态
Activity/Fragment 通常会在以下三种情况被销毁,对于后两种情况我们会希望重建时能恢复之前的页面状态。
| 当前界面永久离开 | 用户跳转至其它界面或直接关闭Activity(调用finish()、点击返回按钮) |
| 配置发生更改 | 屏幕旋转、系统语言等操作会使 Activity 需要立即重建。 |
| 被系统杀死 | 应用在后台因为内存不足被系统杀死,用户返回应用后,Activity 会被重建。 |
二、使用
2.1 获取实例
ViewModel 生命周期长于 Activity/Fragment,如果直接 new 出来就失去了意义。
2.1.1 通过属性委托 by viewModels()
| public inline fun <reified VM : ViewModel> ComponentActivity.viewModels( noinline factoryProducer: (() -> Factory)? = null //自定义工厂用于创建有参ViewModel ): Lazy<VM> |
| public inline fun <reified VM : ViewModel> ComponentActivity.viewModels( noinline extrasProducer: (() -> CreationExtras)? = null, noinline factoryProducer: (() -> Factory)? = null ): Lazy<VM> |
Activity {
val mainViewModel by viewModels<MainViewModel>()
}
2.1.2 (不推荐)通过 ViewModelProvider
调用 get() 获取 ViewModel 实例时,会先用 “包名+类名” 作为 key 到 ViewModelStore 中查找,已经创建过就直接返回(因此同一作用域下重复获取的 ViewModel 为同一实例),没有就进而调用 Factory 的 create() 反射创建一个新的并保存到 ViewModelStore 中。
| ViewModelProvider | 构造创建 |
public constructor( 创建无参 ViewModel 使用。 |
|
public constructor( owner: ViewModelStoreOwner, factory: Factory //自定义工厂用于创建有参ViewModel ) : this(owner.viewModelStore, factory, defaultCreationExtras(owner)) 创建有参 ViewModel 使用。 | ||
|
public constructor( 上面两个都是调用的这个,传入的 defaultCreationExtras 会从 Activity/Fragment 中获取(预设 key 都是从这里注入的,如ComponentActivity 的实现中提供了 Application 和 Intent)。 | ||
| 伴生创建 |
public actual fun create( ): ViewModelProvider = ViewModelProvider(owner.viewModelStore, factory, extras) | |
| public actual fun create( store: ViewModelStore, factory: Factory, extras: CreationExtras ): ViewModelProvider = ViewModelProvider(store, factory, extras) | ||
| get() |
public open operator fun <T : ViewModel> get(modelClass: Class<T>): T public open operator fun <T : ViewModel> get(key: String, modelClass: Class<T>): T 形参 modelClass:需要创建的 ViewModel 的 class(如 MainActivity::class.java)。 形参 key:不指定就是“包名+类名”。 |
Activity {
//构造
val viewModel1 = ViewModelProvider(this).get(DemoViewModel::class)
val viewModel2 = ViewModelProvider(this)[DemoViewModel::class] //推荐
//伴生
val viewModel3 = ViewModelProvider.create(this)[DemoViewModel::class]
}
2.2 ViewModelProvider.Factory
默认 Factory 使用反射创建实例,所以 ViewModel 的构造函数不能有参数 。如需创建带参 ViewModel 需要自定义 Factory。
将自定义的 Factory 放置在其 ViewModel 文件中,以便获得更好的上下文、可读性并使其更容易被发现。
| create() |
public fun <T : ViewModel> create(modelClass: Class<T>): T public fun <T : ViewModel> create(modelClass: Class<T>, extras: CreationExtras): T CreationExtras 是在 Lifecycle v2.5.0 引入的,替代 Factory 为创建 ViewModel 提供所需参数,使得 Factory 无需再持有状态,一个无状态的 Factory 能被更好的复用。(此前为了构建不同参数类型的 ViewModel 而存在各种特殊的 Factory 子类,如提供 Application 的 AndroidViewModelFactory、提供 SavedStateHandle 的AbstractSavedStateViewModelFactory) |
| CreationExtras | APPLICATION_KEY:提供当前 Application context。 |
| VIEW_MODEL_KEY:获取的 String 为在 ViewModelProvider.get() 中传入的 key 参数。可以基于 key 区分多个 ViewModel 实例。 | |
| DEFAULT_ARGS_KEY:获取 Activity 中的 Intent 进而获取 Bundle。createSavedStateHandle 所需的 Bundle。 | |
| SAVED_STATE_REGISTRY_OWNER_KEY:提供创建 createSavedStateHandle 所需的 SavedStateRegistryOwner。 | |
| VIEW_MODEL_STORE_OWNER_KEY:createSavedStateHandle 所需的 ViewModelStoreOwner。 |
2.2.1 普通写法
class DemoRepository(dataSource: DataSource) : IDemoRepository
class DemoViewModel(private val demoRepository: IDemoRepository) : ViewModel()
class DemoFactory(
private val demoRepository: IDemoRepository
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return DemoViewModel(demoRepository) as T
}
}
Activity {
val viewModel1 = DemoFactory(DemoRepository(DataSource())).create(DemoViewModel::class.java)
val viewModel2 = viewModels<DemoViewModel> { DemoFactory(DemoRepository(DataSource())) } //推荐
}
2.2.2 伴生对象写法
class DemoViewModel(
private val demoRepository: IDemoRepository
) : ViewModel() {
companion object {
fun factory = object : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
@Suppress("UNCHECKED_CAST")
return DemoViewModel(DemoRepository(DataSource())) as T
}
}
}
}
Activity {
val demoViewModel by viewModels { factory = DemoViewModel.factory(DataSource()) }
}
2.2.3 DSL 写法
class DemoViewModel(
private val demoRepository: IDemoRepository
) : ViewModel() {
companion object {
fun factory(dataSource: DataSource) = viewModelFactory {
initializer {
DemoViewModel(DemoRepository(dataSource))
}
}
}
}
Activity {
val demoViewModel by viewModels { factory = DemoViewModel.factory(DataSource()) }
}
2.2.4 通用版写法
定义通用工厂(将自定义参数作为 CreationExtras 传递)
class DemoViewModel(repository: DemoRepository) : ViewModel()
private val demoRepository = object : CreationExtras.Key<DemoRepository>{}
@Suppress("UNCHECKED_CAST")
val ViewModelFactory = object : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>, extras: CreationExtras): T {
//通过 key 获取 Application
val myApplication = checkNotNull(extras[APPLICATION_KEY]) as MyApplication
//通过 key 获取 SavedStateHandle
val savedStateHandle = extras.createSavedStateHandle()
//通过 key 获取 Activity 中 Intent 的 Bundle 数据
val bundle = extras[DEFAULT_ARGS_KEY]
//根据不同的 class 类名返回对应的 ViewModel 实例
return when (modelClass) {
DemoViewModel::class.java -> {
//通过 key 获取自定义的参数来创建带参 ViewModel
extras[demoRepository]?.let {
DemoViewModel(it)
}
}
else -> {
throw IllegalArgumentException("Unknown class $modelClass")
}
} as T
}
}
Activity 中使用
//不推荐
Activity {
//通过重写getDefaultViewModelCreationExtras()
//来自定义 ViewModelProvider 构造中的 defaultCreationExtras
//往里面传入提供给自定义 ViewModel 创建时需要的参数
override val defaultViewModelCreationExtras: CreationExtras
get() {
super.defaultViewModelCreationExtras
return MutableCreationExtras(super.defaultViewModelCreationExtras).apply {
set(demoRepository, DemoRepository())
}
}
val viewModel = ViewModelFactory.create(DemoViewModel::class.java)
}
val demoViewmodel1 = ViewModelProvider(
store = this.viewModelStore,
factory = CommonViewModelFactory,
defaultCreationExtras = MutableCreationExtras().apply { set(DEMO_REPOSITORY_KEY, DemoRepository()) }
)[DemoViewModel::class]
val demoViewModel2 by viewModels<DemoViewModel>(
extrasProducer = { MutableCreationExtras().apply { set(DEMO_REPOSITORY_KEY, DemoRepository()) } },
factoryProducer = { CommonViewModelFactory }
)
val demoViewModel3 = CommonViewModelFactory.create(
modelClass = DemoViewModel::class.java,
extras = MutableCreationExtras().apply { set(DEMO_REPOSITORY_KEY, DemoRepository()) }
)
Compose 中使用
@Composable
fun Demo() {
val owner = LocalViewModelStoreOwner.current
val defaultExtras = (owner as? HasDefaultViewModelProviderFactory)?.defaultViewModelCreationExtras ?: CreationExtras.Empty
val extras = MutableCreationExtras(defaultExtras).apply {
set(demoRepository, DemoRepository())
}
val factory = object : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>, extras: CreationExtras): T {
return extras[demoRepository]?.let {
DemoViewModel(it)
}as T
}
}
val viewModel = factory.create(DemoViewModel::class.java, extras)
}
三、ViewModelStoreOwner

在获取 ViewModel 对象时需要传入一个作用域 ViewModelStoreOwner,即保存 ViewModel 实例的地方,可以是 Activity/Fragment/Navigation 它们都有实现该接口。
例如 ComponentActivity 会通过 Lifecycle 观察生命周期 onDestory() 并进行判断,只有在非配置更改的情况下(!isChangeingConfigurations())才会调用 clear() 清除缓存的 ViewModel 列表。这使得 ViewModels 不会因为配置更改而销毁,成为了存储在配置更改后仍然存在的数据的绝佳解决方案,这也是为什么 ViewModel 不适用在后台因为内存不足被系统杀死时的情况(使用 SavedStateHandle 解决)。
- 由于 ViewModel 的生命周期是大于 Activity/Fragment 的, 对于数据的初始化不要放在 UI 生命周期中随着配置更改重建而反复请求,而是放在 ViewModel 自己的 init{ } 代码块中。
3.1 指定作用域
指定为最近的作用域(即获取实例时外层直接包裹它的 )。
val viewModel = ViewModelProvider(this).get(DemoViewModel::class.java)
val viewModel: DemoViewModel by viewModels()
指定为任意作用域。
val viewModel: DemoViewModel by viewModels(
ownerProducer = { requireParentFragment() }
)
//更便捷的在Fragment中将作用域限定为宿主Activity
val viewModel: SharedViewModel by activityViewModels()
指定为 Navigation 图。
val viewModel: DemoViewModel by viewModels(
{ findNavController().getBackStackEntry(R.id.nav_graph) }
)
//更便捷的指定
val viewModel: DemoViewModel by navGraphViewModels(R.id.nav_graph)
//使用 Hilt
val viewModel: SharedViewModel by hiltNavGraphViewModels(R.id.nav_graph)
3.2 同一作用域下重复获取的 ViewModel 为同一实例
同一 Activity 下两个 Fragment 间的通信。
class DemoActivity {
class FragmentA : Fragment() {
val viewModel: DemoViewModel by activityViewModels()
}
class FragmentB : Fragment() {
val viewModel: DemoViewModel by activityViewModels()
}
}
四、SavedStateHandle
在自定义类 ViewModel 的构造中可传入一个 SavedStateHandle 类型参数,和 Bundle 一样以键值对形式存储数据,可以在 APP 处于后台时进程被杀死的情况下幸存下来。
- 简化了状态的保存/还原操作,底层还是由 Activity 中 onSaveInstanceState() 、onCreate() 触发时机。
| contains(key: String) | 检查是否存在给定键的值。 |
| remove(key: String) | 移除给定键的值。 |
| keys() | 返回 SavedStateHandle 中包含的所有键。 |
class DemoViewModel(
private val state: SavedStateHandle
) : ViewModel() {
//普通使用
var name: String
get() = state["name"] ?: ""
set(value) { state["name"] = value }
//编译器会提示推荐使用上面的方式存取
var age: Int
get() = state.get("age") ?: 0
set(value) { state.set("age", value) }
//LiveData
var addressLiveData: LiveData<String> = state.getLiveData("address", "")
//StateFlow
var phoneStateFlow: StateFlow<Long> = state.getStateFlow("phoneNum", 11122223333L)
//Compose
var coinMutableState: Long by state.saveable { mutableStateOf(123L) }
}
class DemoActivity : ComponentActivity(){
val viewModel: DemoViewModel by viewModels()
fun test() {
viewModel.name
viewModel.addressLiveData.observe(this) {}
}
}
五、Compose中使用
- 仅用于最顶层屏幕级组合函数(离 Activity/Fragment 中 setContent() 最近的那个)。
- 遵循唯一可信数据源,ViewModel将状态传递给子组合项,子组合项将事件上抛给父组合项,不要直接将 ViewModel 向下传递给子组合项。
@Composable
fun DemoScreen(
viewModel: DemoViewModel = viewModel(),
viewmodel2: DemoViewModel2 = viewModel(factory = DemoViewModelFactory(DemoRepository()))
) {
Demo(dataState = viewModel.dataState) {
viewModel.buttonClicked()
}
}
@Composable
fun Demo(
dataState: String,
onClick: () -> Unit,
) {
Button(onClick = onClick) { Text(text = dataState) }
}
class DemoViewModel : ViewModel() {
var dataState by mutableStateOf("")
private set
fun buttonClicked() { }
}
文章详细介绍了Android开发中ViewModel的使用,包括其在Activity/Fragment重建时保持状态的能力,如何创建和获取ViewModel实例,特别是ViewModelStoreOwner的作用,以及如何通过Factory创建带有参数的ViewModel。文章还提到了SavedStateHandle在处理配置更改时保存和恢复状态的功能,并简单提及了依赖注入库Hilt在简化ViewModel创建方面的帮助。
1008

被折叠的 条评论
为什么被折叠?



