Я пытаюсь вызвать функцию во фрагменте, который находится в моей ViewModel, но она падает каждый раз, когда ее вызывают, и я не знаю, почему. Вот это код:
Вызов сопрограммы:
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$1$1.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$1$1.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()
Может lifecycleScope.launch(Dispatchers.IO)
? Объекты инициализированы (не нуль)? Также странно, что у вас есть прямой доступ к repository
ViewModel. Может быть, вам стоит сделать это приватным и вызвать insertCourse
.
Любой вызов базы данных должен выполняться вне основного потока. Попробуйте то, что предложил @CoolMind.
Что ж, журнал сбоев говорит, что вы не создали ViewModel. java.lang.Class<domain.CourseViewModel> has no zero argument constructor
. См. stackoverflow.com/questions/44194579/….
Во-первых, вам, вероятно, не нужно загромождать код фрагмента сопрограммой в этой ситуации. Ваша ViewModel и/или репозиторий являются лучшими кандидатами, поскольку они отвечают за так называемую бизнес-логику вашего приложения. Поэтому в идеале измените свою функцию 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, чтобы ViewModelProvider знал, как его создать. В этом Android Kotlin Fundamentals кодовая лаборатория есть пример того, как его создать.
В качестве альтернативы вы можете использовать внедрение зависимостей с помощью Hilt и Jetpack, описанное в здесь.
Давайте посмотрим трассировку стека, пожалуйста, опубликуйте ее.