首页 > 解决方案 > AbstractSavedStateViewModelFactory:已注册具有给定密钥的 SavedStateProvider

问题描述

虽然它是相同的例外,但我的情况与 SavedStateProvider 不同,因为我正在使用 Nav-graph Scoped ViewModels,所以给定的键已经注册,

AbstractSavedStateViewModelFactory与 navGraphViewModels 一起使用时发生异常。
从startFragment,到FirstPageFragment,navigateUp()回到startFragment,然后再次访问FirstPageFragment ->crash

class FirstPageFragment: Fragment() {

    private val myViewModel: MyViewModel by navGraphViewModels(R.id.nav_mission){
        MyViewModel.Factory(requireActivity(), "hello world1")
    }
    ...

我的工厂

class MyViewModel(application: Application,
                  savedStateHandle: SavedStateHandle,
                  val someString: String) : AndroidViewModel(application){

    class Factory(val activity: Activity, val someString: String):
        AbstractSavedStateViewModelFactory(activity as SavedStateRegistryOwner, null) {

        override fun <T : ViewModel?> create(
            key: String,
            modelClass: Class<T>,
            handle: SavedStateHandle
        ): T {
            if (modelClass.isAssignableFrom(MyViewModel::class.java)) {
                @Suppress("UNCHECKED_CAST")
                return MyViewModel(activity.application, handle, someString) as T
            }
            throw IllegalArgumentException("Unable to construct viewmodel")
        }
    }

...
}

这是我的 navGraph,ViewModel 用于 firstPageFragment 和 SecondPageFragment

<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/nav_main_activity"
    app:startDestination="@id/startFragment">

    <fragment
        android:id="@+id/startFragment"
        android:name="com.example.savestatehandledemo.StartFragment"
        android:label="FirstPageFragment" >
        <action
            android:id="@+id/action_startFragment_to_nav_mission"
            app:destination="@id/nav_mission" />
    </fragment>

    <navigation android:id="@+id/nav_mission"
        app:startDestination="@id/firstPageFragment">
        <fragment
            android:id="@+id/firstPageFragment"
            android:name="com.example.savestatehandledemo.FirstPageFragment"
            android:label="FirstPageFragment" >
        </fragment>
        <fragment
            android:id="@+id/secondPageFragment"
            android:name="com.example.savestatehandledemo.SecondPageFragment"
            android:label="SecondPageFragment" >
        </fragment>
    </navigation>
</navigation>

我创建了一个最小的示例来重现该问题。https://github.com/yatw/saveStateHandleDemo/tree/master/app/src/main/java/com/example/savestatehandledemo
此异常仅在进入导航图时发生。

请帮忙!

标签: androidandroid-fragmentsandroidxandroid-viewmodel

解决方案


所以我找到了这个异常的原因,我将活动作为 SavedStateRegistryOwner 传入AbstractSavedStateViewModelFactory
第二次访问 navGraph 时,我传入了相同的活动和内部类SavedStateHandleControllerSavedStateRegistry以某种方式已经保存了状态。 (写这部分的人请解释并写进文档)

所以传入 navGraphgetBackStackEntry
更新了 viewModel 工厂

class MyViewModel(application: Application,
                  savedStateHandle: SavedStateHandle,
                  val someString: String) : AndroidViewModel(application){

    class Factory(val application: Application,
                  val savedStateRegistryOwner: SavedStateRegistryOwner,
                  val someString: String):
        AbstractSavedStateViewModelFactory(
            savedStateRegistryOwner,
            null) {

        override fun <T : ViewModel?> create(
            key: String,
            modelClass: Class<T>,
            handle: SavedStateHandle
        ): T {
            if (modelClass.isAssignableFrom(MyViewModel::class.java)) {
                @Suppress("UNCHECKED_CAST")
                return MyViewModel(application, handle, someString) as T
            }
            throw IllegalArgumentException("Unable to construct viewmodel")
        }
    }

在片段中使用它

class FirstPageFragment: Fragment() {

    private val myViewModel: MyViewModel by navGraphViewModels(R.id.nav_mission){
        MyViewModel.Factory(requireActivity().application,
            findNavController().getBackStackEntry(R.id.nav_mission),
            "hello world1")
    }

特别感谢 EpicPandaForce,https: //stackoverflow.com/a/61649394/5777189


推荐阅读