Caused by: java.lang.RuntimeException: Cannot create an inst



我遇到了一个访问viewmodel的问题。

我有一个活动和2个片段。我有一个活动和片段的视图模型,使用在宿主活动中创建的视图模型的相同实例。

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
}
}
}

如果我在活动函数中使用viewmodel扩展定义viewmodel,它会显示以下错误。

原因:java.lang.RuntimeException: Cannot create an instance of类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]
}
}

也只有当viewmodel in fragment被首先访问时才会发生这种情况。

如果我在oncreate活动之前访问过活动中的viewmodel,在片段中它的工作很好。它能够获得视图模型实例。

class MyActivity : BaseActivity(){
val myViewModel: MyViewModel by viewModels { 
MyViewModelFactory(constructPaymentDataModel()) }
override fun onCreate(savedInstanceState: Bundle?) {
println(myViewModel.isPaymentMethodExists.value)
super.onCreate(savedInstanceState)
}
}

在这里,我在片段访问活动视图模型之前访问视图模型。所以当断点到达这个println方法时,视图模型是由lazy分配的。

如果我先访问片段中的viewmodel,也是一样的。活动中的惰性视图模型没有被分配。

这是总结,如果viewmodel使用viewmodel扩展在activity和fragment中都定义了,并且viewmodel首先在fragment中被访问,它就不能工作了。

当您访问Fragment中的ViewModel时,您没有通过工厂:

private val paymentViewModel: MyViewModel by activityViewModels()

你需要做同样的在Activity-他们都需要能够构建虚拟机,如果有必要,所以它是相同的代码:

// still using the activityViewModels delegate, because you want the Activity's VM instance
private val paymentViewModel: MyViewModel by activityViewModels {
MyViewModelFactory(constructPaymentDataModel())
}

ViewModels的工作方式是每个ActivityFragment类(也是backstack条目,如果你使用Navigation库)可以每个有自己的单个VM实例。这允许同一个Activity或其他对象的不同实例在销毁后抓取相同的VM对象- VM比它们更长寿,并且它们共享它。

它还允许其他组件获取属于Activity(或其他)的特定实例。这就是Fragments如何获取父Activity的特定VM副本并彼此共享数据,因为它们都在查看相同的实例。


要获得虚拟机,您调用ViewModelProvider(owner)(其中owner是拥有实例的ActivityFragment),然后调用get(SomeViewModel::class.java)来说明您想要获取哪种类型的虚拟机。如果已经有一个与这个所有者关联的VM实例,它将返回它-这就是所有东西共享同一个VM对象的方式。

(by viewModelsby activityViewModels只是很好的速记-它们只是分别调用ViewModelProvider(this)viewModelProvider(parentActivity),以通过相关所有者并获得所需的VM)

如果没有此VM的实例,则提供者将创建一个并存储它以供其他任何请求的对象使用。这是关键部分-为特定所有者请求VM实例的第一件事是导致它被创建的原因。如果该创建需要工厂(就像您的情况一样),则第一个请求需要提供该工厂.

因此,当您的Activity是第一个请求VM时,它提供用于创建VM的工厂。然后,当Fragment稍后发出请求时,它不提供工厂功能并不重要- VM实例已经被存储。但是反过来做的话,你就会遇到问题。因为Fragment发出请求,并且不提供工厂,所以它尝试使用默认的无参数工厂。这对于具有PaymentDataModel参数的VM类不起作用,因此您会得到错误。在请求VM的任何地方提供工厂,它就会工作。

希望能澄清!

我想展示一个对我有效的解决方案,也许可以帮助其他面临同样问题的朋友。在我的项目中,我使用的是Hilt。在将@AndroidEntryPoint添加到我调用ViewModel的活动后,问题为我解决了。

相关内容

最新更新