AbstractSavedStateViewModelFactory:已注册具有给定键的 SavedStateProvider
AbstractSavedStateViewModelFactory: SavedStateProvider with the given key is already registered
虽然是同样的异常,但我的情况与不同,因为我使用的是 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
此异常仅在进入导航图时发生。
请帮忙!
所以我找到了这个异常的原因,我在 AbstractSavedStateViewModelFactory
.
中将 activity 作为 SavedStateRegistryOwner 传入
第二次访问 navGraph 时,我传递了相同的 activity 和内部 class SavedStateHandleController
、SavedStateRegistry
以某种方式保存了状态。
(写这部分的人请解释并写入文档)
所以传入导航图getBackStackEntry
更新了视图模型工厂
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,
当你使用 kotlin compose 和 compose navigation 并且想要使用 AbstractSavedStateViewModelFactory 时,你必须通过,就像接受中提到的上面的解决方案,navBackStackEntry。在 compose 中,这是在特定的 composable() { navBackStackEntry -> }
中给出的
工厂示例:
class SavedStateViewModelFactory(
private val repository: LocationRepository,
defaultArgs: Bundle? = null,
savedStateRegistryOwner: SavedStateRegistryOwner) : AbstractSavedStateViewModelFactory(
savedStateRegistryOwner,
defaultArgs) {
override fun <T : ViewModel?> create(
key: String,
modelClass: Class<T>,
handle: SavedStateHandle
): T {
return when {
modelClass.isAssignableFrom(LocationViewModel::class.java) -> {
LocationViewModel(repository, handle) as T
}
modelClass.isAssignableFrom(LocationsViewModel::class.java) -> {
LocationsViewModel(repository, handle) as T
}
else -> throw IllegalArgumentException("wrong ViewModel")
}
}}
setContent() 中的示例:
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colors.background
) {
val navController = rememberNavController()
NavHost(
navController = navController,
startDestination = Screens.LocationsScreen.route,
) {
composable(
route = Screens.LocationsScreen.route
) { navBackStackEntry ->
LocationsScreen(
navController,
viewModel(
modelClass = LocationsViewModel::class.java,
factory = SavedStateViewModelFactory(
repository = LocationRepositoryImpl(
BarCounterDatabase.getINSTANCE(application).locationDao
),
savedStateRegistryOwner = navBackStackEntry
)
)
)
}
composable(
route = Screens.LocationScreen.route+"{id}",
arguments = listOf(
navArgument(
name = "id"
) {
type = NavType.LongType
}
)
) { navBackStackEntry ->
var defaultArgs: Bundle? = null
navBackStackEntry ->.arguments?.getLong("id")?.let { id ->
defaultArgs = Bundle()
defaultArgs?.putLong("id", id)
}
LocationScreen(
navController = navController,
viewModel(
modelClass = LocationViewModel::class.java,
factory = SavedStateViewModelFactory(
repository = LocationRepositoryImpl(
BarCounterDatabase.getINSTANCE(application).locationDao
),
savedStateRegistryOwner = navBackStackEntry,
defaultArgs = defaultArgs
)
)
)
}
}
}
哦,别忘了:我只是根据 BabyishTank
的回答才弄明白的
没有那个答案我还是傻
虽然是同样的异常,但我的情况与
将 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
此异常仅在进入导航图时发生。
请帮忙!
所以我找到了这个异常的原因,我在 AbstractSavedStateViewModelFactory
.
中将 activity 作为 SavedStateRegistryOwner 传入
第二次访问 navGraph 时,我传递了相同的 activity 和内部 class SavedStateHandleController
、SavedStateRegistry
以某种方式保存了状态。
(写这部分的人请解释并写入文档)
所以传入导航图getBackStackEntry
更新了视图模型工厂
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,
当你使用 kotlin compose 和 compose navigation 并且想要使用 AbstractSavedStateViewModelFactory 时,你必须通过,就像接受中提到的上面的解决方案,navBackStackEntry。在 compose 中,这是在特定的 composable() { navBackStackEntry -> }
中给出的工厂示例:
class SavedStateViewModelFactory(
private val repository: LocationRepository,
defaultArgs: Bundle? = null,
savedStateRegistryOwner: SavedStateRegistryOwner) : AbstractSavedStateViewModelFactory(
savedStateRegistryOwner,
defaultArgs) {
override fun <T : ViewModel?> create(
key: String,
modelClass: Class<T>,
handle: SavedStateHandle
): T {
return when {
modelClass.isAssignableFrom(LocationViewModel::class.java) -> {
LocationViewModel(repository, handle) as T
}
modelClass.isAssignableFrom(LocationsViewModel::class.java) -> {
LocationsViewModel(repository, handle) as T
}
else -> throw IllegalArgumentException("wrong ViewModel")
}
}}
setContent() 中的示例:
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colors.background
) {
val navController = rememberNavController()
NavHost(
navController = navController,
startDestination = Screens.LocationsScreen.route,
) {
composable(
route = Screens.LocationsScreen.route
) { navBackStackEntry ->
LocationsScreen(
navController,
viewModel(
modelClass = LocationsViewModel::class.java,
factory = SavedStateViewModelFactory(
repository = LocationRepositoryImpl(
BarCounterDatabase.getINSTANCE(application).locationDao
),
savedStateRegistryOwner = navBackStackEntry
)
)
)
}
composable(
route = Screens.LocationScreen.route+"{id}",
arguments = listOf(
navArgument(
name = "id"
) {
type = NavType.LongType
}
)
) { navBackStackEntry ->
var defaultArgs: Bundle? = null
navBackStackEntry ->.arguments?.getLong("id")?.let { id ->
defaultArgs = Bundle()
defaultArgs?.putLong("id", id)
}
LocationScreen(
navController = navController,
viewModel(
modelClass = LocationViewModel::class.java,
factory = SavedStateViewModelFactory(
repository = LocationRepositoryImpl(
BarCounterDatabase.getINSTANCE(application).locationDao
),
savedStateRegistryOwner = navBackStackEntry,
defaultArgs = defaultArgs
)
)
)
}
}
}
哦,别忘了:我只是根据 BabyishTank
的回答才弄明白的没有那个答案我还是傻