【问题标题】:Cannot create an instance of class ViewModel unable to find cause无法创建类 ViewModel 的实例无法找到原因
【发布时间】:2022-08-16 16:08:35
【问题描述】:

每当我单击标题时,应用程序崩溃都会显示此 logcat 我是安卓新手 使用 匕首柄, 外线播放器,

这是我接触错误的地方:

歌曲片段

@AndroidEntryPoint
class SongFragments : Fragment(R.layout.fragment_song) {

    @Inject
    lateinit var glide: RequestManager

    private lateinit var mainViewModel: MainViewModel

    private val songViewModel: SongViewModel by viewModels()


    private var curplayingSong: sound? = null

    private var playbackState: PlaybackStateCompat? = null

    private var shouldUpdateSeekbar = true

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        mainViewModel = ViewModelProvider(requireActivity()).get(MainViewModel::class.java)
        subscribeToObservers()


        ivPlayPauseDetail.setOnClickListener {
            curplayingSong?.let {
                mainViewModel.playOrToggleSound(it, true)
            }
        }

       seekBar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
           override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
               if (fromUser) {
                   setCurPlayerTimeToTextView(progress.toLong())
               }
           }

           override fun onStartTrackingTouch(seekBar: SeekBar?) {
              shouldUpdateSeekbar = false

           }

           override fun onStopTrackingTouch(seekBar: SeekBar?) {
              seekBar?.let {
                  mainViewModel.seekTo(it.progress.toLong())
                  shouldUpdateSeekbar = true
              }
           }

       })

        ivSkipPrevious.setOnClickListener {
            mainViewModel.skipToPreviousSound()
        }
        ivSkip.setOnClickListener{
            mainViewModel.skipToNextSound()
        }
    }



    private fun updateTitleAndSongImage(sound: sound) {
        val title = \"${sound.title} - ${sound.subtitle}\"
        tvSongName.text = title
        glide.load(sound.imageUrl).into(ivSongImage)
    }

    private fun subscribeToObservers() {
        mainViewModel.mediaItems.observe(viewLifecycleOwner){
            it?.let { result ->
                when(result.status){
                    Status.SUCCESS -> {
                       result.data?.let { sounds ->
                           if (curplayingSong == null && sounds.isNotEmpty()) {
                               curplayingSong = sounds[0]
                               updateTitleAndSongImage(sounds[0])
                           }
                       }
                    }
                    else -> Unit
                }
            }
        }
     mainViewModel.curPlayingSound.observe(viewLifecycleOwner) {
         if(it == null) return@observe
         curplayingSong = it.toSong()
         updateTitleAndSongImage(curplayingSong!!)
     }

        mainViewModel.playbackState.observe(viewLifecycleOwner) {
          playbackState = it
            ivPlayPauseDetail.setImageResource(
                if (playbackState?.isPlaying == true) R.drawable.ic_pause else R.drawable.ic_play
            )
            seekBar.progress = it?.position?.toInt() ?: 0
        }

        songViewModel.curPlayerPosition.observe(viewLifecycleOwner) {
           if (shouldUpdateSeekbar) {
               seekBar.progress = it.toInt()
               setCurPlayerTimeToTextView(it)
           }
        }
        songViewModel.curSongDuration.observe(viewLifecycleOwner) {
            seekBar.max = it.toInt()
            val dateFormat = SimpleDateFormat(\"mm:ss\", Locale.getDefault())
            tvSongDuration.text = dateFormat.format(it)
        }

    }

    private fun  setCurPlayerTimeToTextView(ms: Long) {
        val dateFormat = SimpleDateFormat(\"mm:ss\", Locale.getDefault())
        tvCurTime.text = dateFormat.format(ms)
    }


}

SongviewModel

class SongViewModel @ViewModelInject constructor(
    musicServiceConnection: MusicServiceConnection
) : ViewModel() {

    private val playbackState = musicServiceConnection.playBackState

    private  val _curSongDuration = MutableLiveData<Long>()
    val curSongDuration: LiveData<Long> = _curSongDuration

    private val _curPlayerPosition = MutableLiveData<Long>()
    val curPlayerPosition: LiveData<Long> = _curPlayerPosition

    init {
        updateCurrentplayerPostion()
    }

    @SuppressLint(\"NullSafeMutableLiveData\")
    private fun updateCurrentplayerPostion() {
        viewModelScope.launch {
            while(true) {
             val pos = playbackState.value?.currentPlaybackPosition
                if(curPlayerPosition.value != pos){
                    _curPlayerPosition.postValue(pos)
                    _curSongDuration.postValue(MusicService.curSoundDuration)
                }
                delay(UPDATE_PLAYER_POSITION_INTERVAL)
            }
        }
    }
}

日志猫:

2022-08-12 19:11:39.122 10888-10983/com.fridayhouse.snoozz E/ion: ioctl c0044901 failed with code -1: Invalid argument
2022-08-12 19:11:39.318 10888-10888/com.fridayhouse.snoozz E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.fridayhouse.snoozz, PID: 10888
    java.lang.RuntimeException: Cannot create an instance of class com.fridayhouse.snoozz.ui.viewmodels.SongViewModel
        at androidx.lifecycle.ViewModelProvider$NewInstanceFactory.create(ViewModelProvider.kt:204)
        at androidx.lifecycle.ViewModelProvider$AndroidViewModelFactory.create(ViewModelProvider.kt:322)
        at androidx.lifecycle.ViewModelProvider$AndroidViewModelFactory.create(ViewModelProvider.kt:304)
        at androidx.lifecycle.SavedStateViewModelFactory.create(SavedStateViewModelFactory.kt:175)
        at androidx.lifecycle.SavedStateViewModelFactory.create(SavedStateViewModelFactory.kt:203)
        at dagger.hilt.android.internal.lifecycle.HiltViewModelFactory.create(HiltViewModelFactory.java:111)
        at androidx.lifecycle.ViewModelProvider$Factory.create(ViewModelProvider.kt:83)
        at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.kt:187)
        at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.kt:153)
        at androidx.lifecycle.ViewModelLazy.getValue(ViewModelLazy.kt:53)
        at androidx.lifecycle.ViewModelLazy.getValue(ViewModelLazy.kt:35)
        at com.fridayhouse.snoozz.ui.fragments.SongFragments.getSongViewModel(SongFragments.kt:33)
        at com.fridayhouse.snoozz.ui.fragments.SongFragments.subscribeToObservers(SongFragments.kt:121)
        at com.fridayhouse.snoozz.ui.fragments.SongFragments.onViewCreated(SongFragments.kt:45)
        at androidx.fragment.app.Fragment.performViewCreated(Fragment.java:3128)
        at androidx.fragment.app.FragmentStateManager.createView(FragmentStateManager.java:552)
        at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:261)
        at androidx.fragment.app.FragmentManager.executeOpsTogether(FragmentManager.java:1890)
        at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:1814)
        at androidx.fragment.app.FragmentManager.execPendingActions(FragmentManager.java:1751)
        at androidx.fragment.app.FragmentManager$5.run(FragmentManager.java:538)
        at android.os.Handler.handleCallback(Handler.java:938)
        at android.os.Handler.dispatchMessage(Handler.java:99)
        at android.os.Looper.loopOnce(Looper.java:233)
        at android.os.Looper.loop(Looper.java:334)
        at android.app.ActivityThread.main(ActivityThread.java:8396)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:582)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1068)
     Caused by: java.lang.InstantiationException: java.lang.Class<com.fridayhouse.snoozz.ui.viewmodels.SongViewModel> has no zero argument constructor
        at java.lang.Class.newInstance(Native Method)
        at androidx.lifecycle.ViewModelProvider$NewInstanceFactory.create(ViewModelProvider.kt:202)
        at androidx.lifecycle.ViewModelProvider$AndroidViewModelFactory.create(ViewModelProvider.kt:322) 
        at androidx.lifecycle.ViewModelProvider$AndroidViewModelFactory.create(ViewModelProvider.kt:304) 
        at androidx.lifecycle.SavedStateViewModelFactory.create(SavedStateViewModelFactory.kt:175) 
        at androidx.lifecycle.SavedStateViewModelFactory.create(SavedStateViewModelFactory.kt:203) 
        at dagger.hilt.android.internal.lifecycle.HiltViewModelFactory.create(HiltViewModelFactory.java:111) 
        at androidx.lifecycle.ViewModelProvider$Factory.create(ViewModelProvider.kt:83) 
        at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.kt:187) 
        at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.kt:153) 
        at androidx.lifecycle.ViewModelLazy.getValue(ViewModelLazy.kt:53) 
        at androidx.lifecycle.ViewModelLazy.getValue(ViewModelLazy.kt:35) 
        at com.fridayhouse.snoozz.ui.fragments.SongFragments.getSongViewModel(SongFragments.kt:33) 
        at com.fridayhouse.snoozz.ui.fragments.SongFragments.subscribeToObservers(SongFragments.kt:121) 
        at com.fridayhouse.snoozz.ui.fragments.SongFragments.onViewCreated(SongFragments.kt:45) 
        at androidx.fragment.app.Fragment.performViewCreated(Fragment.java:3128) 
        at androidx.fragment.app.FragmentStateManager.createView(FragmentStateManager.java:552) 
        at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:261) 
        at androidx.fragment.app.FragmentManager.executeOpsTogether(FragmentManager.java:1890) 
        at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:1814) 
        at androidx.fragment.app.FragmentManager.execPendingActions(FragmentManager.java:1751) 
        at androidx.fragment.app.FragmentManager$5.run(FragmentManager.java:538) 
        at android.os.Handler.handleCallback(Handler.java:938) 
        at android.os.Handler.dispatchMessage(Handler.java:99) 
        at android.os.Looper.loopOnce(Looper.java:233) 
        at android.os.Looper.loop(Looper.java:334) 
        at android.app.ActivityThread.main(ActivityThread.java:8396) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:582) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1068) 
  • 我修复了您的代码格式。仅供参考,您应该用三重反引号 ``` 包围代码,而不是三撇号 \'\'\'
  • 请添加您的 MusicServiceConnection 代码。你是如何用刀柄注射的?
  • 请检查我的答案,让我知道它是否有效。

标签: android kotlin mvvm android-viewmodel dagger-hilt


【解决方案1】:

由于您使用的是 Hilt,因此您不应该使用 by viewModels()。你应该在你的 Provider 中设置你的MusicServiceConnection,而不是使用private val songViewModel: SongViewModel by viewModels(),而是使用@Inject private lateinit var songViewModel: SongViewModel

我对 Hilt 不是很熟悉,所以无法提供更多指导。下面解释了by viewModels() 出了什么问题,以及如果您遇到此问题,您将如何解决不是使用依赖注入框架。


当您在 Activity 或 Fragment 中创建 ViewModel 时,您可以提供一个负责创建 ViewModel 实例的工厂。如果您不提供工厂,则使用默认工厂。

如果您的 ViewModel 构造函数的参数是以下之一,则默认工厂只能创建 ViewModel 的实例:

  • constructor() 空构造函数(无参数)
  • constructor(savedStateHandle: SavedStateHandle)
  • constructor(application: Application)
  • constructor(application: Application, savedStateHandle: SavedStateHandle)

我认为这没有很好的记录。我不得不查看 Jetpack 源代码来学习它。

由于您的 ViewModel 需要 MusicServiceConnection 参数,因此您不能使用默认工厂。您需要将明确的ViewModelProvider.Factory 作为参数提供给by viewModels()

This training 有一个为需要构造函数的特殊参数的 ViewModel 创建 ViewModelProvider.Factory 类的示例。在您的情况下,解决方案可能如下所示:

class SongViewModelFactory(private val musicServiceConnection: MusicServiceConnection) : ViewModelProvider.Factory {
    override fun <T : ViewModel> create(modelClass: Class<T>): T {
        require(modelClass.isAssignableFrom(SongViewModel::class.java)) { "Unknown ViewModel class" }
        @Suppress("UNCHECKED_CAST")
        return SongViewModel(musicServiceConnection) as T
    }
}

我不知道您的 MusicServiceConnection 类来自哪里,但让我们假设您可以使用空构造函数为您的 Activity 简单地实例化一次。然后你会更换

private val songViewModel: SongViewModel by viewModels()

private val songViewModel: SongViewModel by viewModels {
    SongViewModelFactory(MusicServiceConnection())
}

请注意,您将 lambda 函数传递给 viewModels,而不是直接传递工厂的实例。这是因为工厂只是懒惰地创建了一次,即使你的 Activity 被重新创建了多次(比如用户来回旋转屏幕)。此 lambda 中的代码仅在应用导航到此屏幕时调用一次,即使为屏幕旋转或其他配置更改重新创建 Activity 也是如此。

【讨论】:

  • 嘿@Tenfour04,我仍然没有摆脱这个错误也许我应该更彻底地学习viewModels,在实施它之前,顺便说一句谢谢你的解释这里是我的项目[项目](github.com/th3kumar/Snoozz-Sleeping-Buddy)请看看并指导我
  • 如果您切换到@Inject,您应该会收到不同的错误(如果有任何错误)。我不使用 Hilt,所以对此无济于事。查看使用 Hilt 时如何注入视图模型。也许您忘记将 MusicServiceConnection 添加到您的提供商。
【解决方案2】:

@ViewModelInject 已被弃用。您可以check here 了解更多信息。

在较新的版本中,您需要使用@AndroidViewModel 注释视图模型类,并使用@Inject 注释构造函数。

@AndroidViewModel
class SongViewModel @Inject constructor(
    musicServiceConnection: MusicServiceConnection
) : ViewModel() 

希望它能解决这个问题。

【讨论】:

    猜你喜欢
    • 2021-11-28
    • 2017-12-13
    • 1970-01-01
    • 2018-11-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多