为什么我在 kotlin 中的应用程序在调用协程时会崩溃?

Why my app in kotlin crashes when calling a coroutine?

我试图在我的 ViewModel 中的 Fragment 中调用一个函数,但每次调用时它都会崩溃,我不知道为什么。 这是代码:

调用协程:

binding.button.setOnClickListener {
            lifecycleScope.launch(Dispatchers.IO){
                courseViewModel.repository.insertCourse(getData())
            }
            Navigation.findNavController(it).popBackStack()
        }

函数代码: suspend fun insertCourse(course: Course) = courseDAO.insertCourse(course)

如果我不使用协程而只是 courseViewModel.repository.insertCourse(getData()) 我会收到一条错误消息,提示我必须从其他挂起函数或协程调用此函数。

这是错误:

2022-03-20 13:33:43.893 28396-28437/com.example.cursosmvvm E/AndroidRuntime: FATAL EXCEPTION: DefaultDispatcher-worker-1
    Process: com.example.cursosmvvm, PID: 28396
    java.lang.RuntimeException: Cannot create an instance of class domain.CourseViewModel
        at androidx.lifecycle.ViewModelProvider$NewInstanceFactory.create(ViewModelProvider.kt:188)
        at androidx.lifecycle.ViewModelProvider$AndroidViewModelFactory.create(ViewModelProvider.kt:238)
        at androidx.lifecycle.SavedStateViewModelFactory.create(SavedStateViewModelFactory.java:112)
        at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.kt:169)
        at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.kt:139)
        at androidx.lifecycle.ViewModelLazy.getValue(ViewModelLazy.kt:44)
        at androidx.lifecycle.ViewModelLazy.getValue(ViewModelLazy.kt:31)
        at ui.NewCourseFragment.getCourseViewModel(NewCourseFragment.kt:23)
        at ui.NewCourseFragment.access$getCourseViewModel(NewCourseFragment.kt:20)
        at ui.NewCourseFragment$onViewCreated.invokeSuspend(NewCourseFragment.kt:58)
        at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
        at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
        at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:571)
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:750)
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:678)
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:665)
     Caused by: java.lang.InstantiationException: java.lang.Class<domain.CourseViewModel> has no zero argument constructor
        at java.lang.Class.newInstance(Native Method)
        at androidx.lifecycle.ViewModelProvider$NewInstanceFactory.create(ViewModelProvider.kt:186)
        at androidx.lifecycle.ViewModelProvider$AndroidViewModelFactory.create(ViewModelProvider.kt:238) 
        at androidx.lifecycle.SavedStateViewModelFactory.create(SavedStateViewModelFactory.java:112) 
        at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.kt:169) 
        at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.kt:139) 
        at androidx.lifecycle.ViewModelLazy.getValue(ViewModelLazy.kt:44) 
        at androidx.lifecycle.ViewModelLazy.getValue(ViewModelLazy.kt:31) 
        at ui.NewCourseFragment.getCourseViewModel(NewCourseFragment.kt:23) 
        at ui.NewCourseFragment.access$getCourseViewModel(NewCourseFragment.kt:20) 
        at ui.NewCourseFragment$onViewCreated.invokeSuspend(NewCourseFragment.kt:58) 
        at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) 
        at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106) 
        at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:571) 
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:750) 
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:678) 
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:665) 

我的 ViewModel 是这样的:

class CourseViewModel(val repository: CourseRepository): ViewModel(){

在我调用协程的片段中,我这样声明它:private val courseViewModel: CourseViewModel by activityViewModels()

首先,在这种情况下,您可能不需要用协程来弄乱您的片段代码。您的 ViewModel and/or 存储库是更好的候选者,因为它们负责您应用程序的 so-called 业务逻辑。所以理想情况下将您的 ViewModel 函数更改为使用 viewModelScope.launch(Dispatchers.IO) 然后您的片段代码可以简单地是:

binding.button.setOnClickListener {
    courseViewModel.insertCourse(getData())
    Navigation.findNavController(it).popBackStack()
}

现在关于您的 ViewModel,您说您将其声明为:

class CourseViewModel(val repository: CourseRepository): ViewModel()

但是,默认的 ViewModelProvider(by activityViewModels()by viewModels() 使用它来创建您的 ViewModel)只会实例化一个 零参数 ViewModel ,这解释了您看到的错误。 (旁注:Google 当前关于所有这些工作原理的文档简直太糟糕了;没有更好的表达方式了。)

如果您想将存储库作为参数保留,则需要创建一个自定义 ViewModelFactory so that the ViewModelProvider knows how to instantiate it. This Android Kotlin Fundamentals codelab 有一个如何创建的示例。

作为替代方法,您可以将依赖项注入与 Hilt 和 Jetpack 结合使用,如 here 所述。