ViewModel 和进程死亡
ViewModel and process death
为什么建议使用 ViewModel
架构组件,如果它不能处理 process death
?
例如,如果我用 onSaveInstanceState
保存状态,configuration change
和 process death
的状态都会持续存在,而 ViewModel
只能存活 configuration change
,才能存活process death
需要从 ViewModel
获取状态并使用相同的 onSaveInstanceState
机制。
我错过了什么?
Why it is recommend to use ViewModel architecture component, if it not handle process death?
What do I miss?
你是对的,直到 2020 年 1 月发生了,他们最终发布了 viewmodel-savedstate:1.0.0
然后制作了 AndroidX Activity,Fragment 和 Navigation 最新的库版本提供 SavedStateViewModelFactory
作为默认 [=18] =].
现在您可以将 SavedStateHandle
放入 ViewModel
中,这可以帮助解决 persisting/restoring 跨进程死亡的问题。
class MyViewModel(private val savedStateHandle: SavedStateHandle): ViewModel() {
val someState: MutableLiveData<String> = savedStateHandle.getLiveData("someState")
}
或
class MyAndroidViewModel(application: Application, private val savedStateHandle: SavedStateHandle): AndroidViewModel(application) {
val someState: MutableLiveData<String> = savedStateHandle.getLiveData("someState")
}
这些 可以通过它们的默认构造函数 工作,因为 SavedStateViewModelFactory
通过反射创建它们。
如果您需要 SavedStateHandle
和其他参数,则可以不使用默认 ViewModelProvider.Factory
,而是从 AbstractSavedStateViewModelFactory
扩展,这将为您提供 SavedStateHandle
实际有效并且 persists/restores 跨进程自动死亡。
val viewModel = ViewModelProvider(this, myAbstractSavedStateViewModelFactoryImpl).get(MyViewModel::class.java)
或使用 KTX 东西
private val viewModel by viewModels { myAbstractSavedStateViewModelFactoryImpl }
您还可以将 ViewModel 的范围限定为 Jetpack Navigation 的 NavGraph,这有点棘手但可行:
inline fun <reified T : ViewModel> SavedStateRegistryOwner.createAbstractSavedStateViewModelFactory(
arguments: Bundle,
crossinline creator: (SavedStateHandle) -> T
): ViewModelProvider.Factory {
return object : AbstractSavedStateViewModelFactory(this, arguments) {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel?> create(
key: String, modelClass: Class<T>, handle: SavedStateHandle
): T = creator(handle) as T
}
}
inline fun <reified T : ViewModel> Fragment.navGraphSavedStateViewModels(
@IdRes navGraphId: Int,
crossinline creator: (SavedStateHandle) -> T
): Lazy<T> {
// Wrapped in lazy to not search the NavController each time we want the backStackEntry
val backStackEntry by lazy { findNavController().getBackStackEntry(navGraphId) }
return createViewModelLazy(T::class, storeProducer = {
backStackEntry.viewModelStore
}, factoryProducer = {
backStackEntry.createAbstractSavedStateViewModelFactory(
arguments = backStackEntry.arguments ?: Bundle(), creator = creator
)
})
}
然后
class MyFragment: Fragment() {
private val mySharedViewModel by navGraphSavedStateViewModels(R.id.registration_graph) { handle ->
MySharedViewModel(handle)
}
}
为什么建议使用 ViewModel
架构组件,如果它不能处理 process death
?
例如,如果我用 onSaveInstanceState
保存状态,configuration change
和 process death
的状态都会持续存在,而 ViewModel
只能存活 configuration change
,才能存活process death
需要从 ViewModel
获取状态并使用相同的 onSaveInstanceState
机制。
我错过了什么?
Why it is recommend to use ViewModel architecture component, if it not handle process death?
What do I miss?
你是对的,直到 2020 年 1 月发生了,他们最终发布了 viewmodel-savedstate:1.0.0
然后制作了 AndroidX Activity,Fragment 和 Navigation 最新的库版本提供 SavedStateViewModelFactory
作为默认 [=18] =].
现在您可以将 SavedStateHandle
放入 ViewModel
中,这可以帮助解决 persisting/restoring 跨进程死亡的问题。
class MyViewModel(private val savedStateHandle: SavedStateHandle): ViewModel() {
val someState: MutableLiveData<String> = savedStateHandle.getLiveData("someState")
}
或
class MyAndroidViewModel(application: Application, private val savedStateHandle: SavedStateHandle): AndroidViewModel(application) {
val someState: MutableLiveData<String> = savedStateHandle.getLiveData("someState")
}
这些 可以通过它们的默认构造函数 工作,因为 SavedStateViewModelFactory
通过反射创建它们。
如果您需要 SavedStateHandle
和其他参数,则可以不使用默认 ViewModelProvider.Factory
,而是从 AbstractSavedStateViewModelFactory
扩展,这将为您提供 SavedStateHandle
实际有效并且 persists/restores 跨进程自动死亡。
val viewModel = ViewModelProvider(this, myAbstractSavedStateViewModelFactoryImpl).get(MyViewModel::class.java)
或使用 KTX 东西
private val viewModel by viewModels { myAbstractSavedStateViewModelFactoryImpl }
您还可以将 ViewModel 的范围限定为 Jetpack Navigation 的 NavGraph,这有点棘手但可行:
inline fun <reified T : ViewModel> SavedStateRegistryOwner.createAbstractSavedStateViewModelFactory(
arguments: Bundle,
crossinline creator: (SavedStateHandle) -> T
): ViewModelProvider.Factory {
return object : AbstractSavedStateViewModelFactory(this, arguments) {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel?> create(
key: String, modelClass: Class<T>, handle: SavedStateHandle
): T = creator(handle) as T
}
}
inline fun <reified T : ViewModel> Fragment.navGraphSavedStateViewModels(
@IdRes navGraphId: Int,
crossinline creator: (SavedStateHandle) -> T
): Lazy<T> {
// Wrapped in lazy to not search the NavController each time we want the backStackEntry
val backStackEntry by lazy { findNavController().getBackStackEntry(navGraphId) }
return createViewModelLazy(T::class, storeProducer = {
backStackEntry.viewModelStore
}, factoryProducer = {
backStackEntry.createAbstractSavedStateViewModelFactory(
arguments = backStackEntry.arguments ?: Bundle(), creator = creator
)
})
}
然后
class MyFragment: Fragment() {
private val mySharedViewModel by navGraphSavedStateViewModels(R.id.registration_graph) { handle ->
MySharedViewModel(handle)
}
}