ViewModel 和进程死亡

ViewModel and process death

为什么建议使用 ViewModel 架构组件,如果它不能处理 process death

例如,如果我用 onSaveInstanceState 保存状态,configuration changeprocess 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)
    }
}