我遇到了访问 View 模型的问题。
我有一个 Activity 和 2 个 fragment 。 我有一个用于 Activity 和 fragment 的 View 模型,使用在主机 Activity 中创建的相同 View 模型实例。
class MyViewModel(var paymentDataModel: PaymentDataModel) : ViewModel(){
fun someMethod():Boolean{
//return Something
}
}
class MyViewModelFactory(var paymentDataModel: PaymentDataModel) : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return MyViewModel(paymentDataModel) as T
}
}
class NewPaymentAmountFragment : Fragment() {
private val paymentViewModel: MyViewModel by activityViewModels()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
if(paymentViewModel.someMehtod()){
//Accessing activity viewmodel in fragment
}
}
}
如果我在 Activity 函数中使用 viewModel 扩展定义 viewmodel,它会显示以下错误。
Caused by: java.lang.RuntimeException: Cannot create an instance of class com.app.MyViewModel
class MyActivity : BaseActivity(){
val myViewModel: MyViewModel by viewModels {
MyViewModelFactory(constructPaymentDataModel()) }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
}
但是,如果我使用 ViewModelProvider() 以正常方式定义 ViewModel,它就可以正常工作。
class MyActivity : BaseActivity(){
lateint var myViewModel: MyViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val viewModelFactory = MyViewModelFactory(constructPaymentDataModel())
myViewModel = ViewModelProvider(this, viewModelFactory)[MyViewModel::class.java]
}
}
只有当首先访问 fragment 中的 viewmodel 时才会发生这种情况。
如果我在 oncreate of activity 之前访问过一次 activity 中的 viewmodel,则在 fragment 中它工作正常。它能够获取 View 模型实例。
class MyActivity : BaseActivity(){
val myViewModel: MyViewModel by viewModels {
MyViewModelFactory(constructPaymentDataModel()) }
override fun onCreate(savedInstanceState: Bundle?) {
println(myViewModel.isPaymentMethodExists.value)
super.onCreate(savedInstanceState)
}
}
在这里,我在 fragment 访问 Activity View 模型之前访问了 View 模型。 所以这里的viewmodel是在断点到这个println方法的时候被lazy赋值的。
相同,如果我首先访问 fragment 中的 View 模型。 Activity 中的惰性 View 模型未分配。
总结如下 如果使用 View 模型扩展在 Activity 和 fragment 中都定义了 View 模型,并且首先在 fragment 中访问 View 模型,则它不起作用。
最佳答案
当您访问 Fragment
中的 ViewModel
时,您没有通过工厂:
private val paymentViewModel: MyViewModel by activityViewModels()
您需要执行与 Activity
中相同的操作 - 如果需要,它们都需要能够构建 VM,因此代码相同:
// still using the activityViewModels delegate, because you want the Activity's VM instance
private val paymentViewModel: MyViewModel by activityViewModels {
MyViewModelFactory(constructPaymentDataModel())
}
ViewModel
的工作方式是每个 Activity
和 Fragment
类(如果您使用 Navigation library) 每个都可以有自己的 VM 实例。这允许相同 Activity
或任何东西的不同实例在被销毁后获取相同的 VM 对象 - VM 比它们长,并且它们共享它。
它还允许其他组件获取属于 Activity
(或其他)的特定实例。这就是 Fragment 可以获取其父 Activity 的特定 VM 副本并彼此共享数据的方式,因为它们都在查看同一个实例。
要获得 VM,您调用 ViewModelProvider(owner)
(其中 owner
是 Activity
或 Fragment
拥有该实例)然后调用 get(SomeViewModel::class.java)
来说明您想要获取哪种类型的 VM。 如果已经有与该所有者关联的该 VM 的实例,它将返回它 - 这就是所有东西共享同一个 VM 对象的方式。
(by viewModels
和 by activityViewModels
只是很好的简写 - 它们只是调用 ViewModelProvider(this)
或 viewModelProvider( parentActivity)
分别传递相关的owner,获取需要的VM)
如果没有此 VM 的实例,则提供商将创建一个并将其存储以供其他任何请求它的实例使用。 这是关键部分 - 为特定所有者请求 VM 实例的第一件事是创建它的原因。如果该创建需要工厂(如您的情况),第一个请求需要提供该工厂。
因此,当您的 Activity
是第一个请求 VM 时,它会提供用于创建它的工厂。然后当 Fragment
稍后发出请求时,它不提供工厂功能并不重要 - VM 实例已经存储。但是反过来做,你就会遇到问题。因为 Fragment
发出请求,但不提供工厂,它会尝试使用默认的无参数工厂。这对带有 PaymentDataModel
参数的 VM 类不起作用,因此您会收到错误消息。在您请求 VM 的任何地方提供工厂,它就会工作。
希望一切顺利!
关于android - 由 : java. lang.RuntimeException : Cannot create an instance of class com. app.MyViewModel 引起,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/74548705/