导致:java.lang.RuntimeException:无法创建类 com.app.MyViewModel 的实例

Gow*_*K K 3 android mvvm viewmodel kotlin android-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
     }
    }
}
Run Code Online (Sandbox Code Playgroud)

如果我在活动函数中使用 viewModel 扩展定义 viewmodel,则会出现以下错误。

导致:java.lang.RuntimeException:无法创建类 com.app.MyViewModel 的实例

    class MyActivity : BaseActivity(){
    
    val myViewModel: MyViewModel by viewModels { 
    MyViewModelFactory(constructPaymentDataModel()) }

override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
        }
    }
Run Code Online (Sandbox Code Playgroud)

但是如果我使用 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]
    }
}
Run Code Online (Sandbox Code Playgroud)

此外,仅当首先访问片段中的视图模型时才会发生这种情况。

如果我在 Activity 的 oncreate 之前访问了一次 Activity 中的视图模型,那么在片段中它工作正常。它能够获取视图模型实例。

class MyActivity : BaseActivity(){

val myViewModel: MyViewModel by viewModels { 
MyViewModelFactory(constructPaymentDataModel()) }

 override fun onCreate(savedInstanceState: Bundle?) {
        println(myViewModel.isPaymentMethodExists.value)
        super.onCreate(savedInstanceState)
}
}
Run Code Online (Sandbox Code Playgroud)

在这里,我在片段访问活动视图模型之前访问了视图模型。所以这里viewmodel是在断点到这个println方法的时候通过lazy赋值的。

同样,如果我首先访问片段中的视图模型。活动中的惰性视图模型不会被分配。

所以这里是总结,如果使用视图模型扩展在活动和片段中定义视图模型,并且首先在片段中访问视图模型,则它不起作用。

cac*_*acs 6

ViewModel当您访问以下位置时,您不会通过工厂Fragment

private val paymentViewModel: MyViewModel by activityViewModels()
Run Code Online (Sandbox Code Playgroud)

您需要执行与中相同的操作Activity- 如果需要,它们都需要能够构建虚拟机,因此它是相同的代码:

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

ViewModel其工作方式是Activity每个Fragment类(如果您使用导航库,还有返回堆栈条目)都可以拥有自己的单个 VM 实例。这允许相同对象Activity或其他对象的不同实例在被销毁后获取相同的虚拟机对象 - 虚拟机的寿命比它们长,并且它们共享它。

它还允许其他组件获取属于Activity(或其他)的特定实例。这就是片段如何获取其父活动的特定虚拟机副本并相互共享数据的方式,因为它们都在查看同一个实例。


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

by viewModelsby activityViewModels是很好的简写 - 他们只是分别调用ViewModelProvider(this)viewModelProvider(parentActivity)来传递相关所有者并获取所需的虚拟机)

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

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

希望能解决这个问题!

  • @GowthamKK 是的,我想这就是你的意思,但它们并不是以这种方式连接的(并且“Fragment”意味着可以在任何“Activity”中工作,因此很容易损坏!)。“by ActivityViewModels”内容隐藏了它实际的工作原理,但是一旦您知道有一个单独的组件,就像“MyActivity's ViewModels”等的储藏室一样,它就有意义了 (2认同)