2

whenever I click on the title, the app Crashes shows this logcat I am new to Android using dagger hilt, exoplayer,

this is where i touch got error:

 image (please look)

Songfragment

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

logcat:

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) 
Tenfour04
  • 83,111
  • 11
  • 94
  • 154
Sidharth
  • 79
  • 10
  • I fixed your code formatting. FYI, you should surround code with triple-backticks `\`\`\``, not triple-apostrophes `'''`. – Tenfour04 Aug 12 '22 at 14:29
  • Please add your MusicServiceConnection code. And how you are injecting with hilt? – Gowtham K K Aug 13 '22 at 10:36
  • @GowthamKK [MusicServiceConnection](https://github.com/th3kumar/Snoozz-Sleeping-Buddy/blob/master/app/src/main/java/com/fridayhouse/snoozz/exoplayer/MusicServiceConnection.kt) – Sidharth Aug 16 '22 at 07:47
  • Please check my answer and let me know if its working or not. – Gowtham K K Aug 16 '22 at 08:09
  • hey @GowthamKK, hope you are safe and fine. is there anything you could help with in [stackOverflow Question](https://stackoverflow.com/questions/74046324/unresolved-reference-exoplayerfactory-in-exoplayerfactory-newsimpleinstance-and) any help will be highly appreciated. – Sidharth Oct 14 '22 at 07:48

2 Answers2

1

@ViewModelInject has been deprecated. You can check here for more info.

In newer versions, You need to annotate viewmodel class with @AndroidViewModel and constructor with @Inject.

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

Hope it would solve this issue.

Gowtham K K
  • 3,123
  • 1
  • 11
  • 29
0

Since you are using Hilt, you shouldn't be using by viewModels(). You should set up your MusicServiceConnection in your Provider, and instead of using private val songViewModel: SongViewModel by viewModels(), use @Inject private lateinit var songViewModel: SongViewModel.

I'm not very familiar with Hilt, so I can't provide more guidance. The below is an explanation of what's going wrong with by viewModels(), and how you would resolve this if you were not using a dependency injection framework.


When you create a ViewModel in your Activity or Fragment, you can supply a factory that is responsible for creating instances of the ViewModel. If you don't supply a factory, then the default factory is used.

The default factory is only capable of creating instances of your ViewModel if your ViewModel constructor's arguments are one of the following:

  • constructor() Empty constructor (no arguments)
  • constructor(savedStateHandle: SavedStateHandle)
  • constructor(application: Application)
  • constructor(application: Application, savedStateHandle: SavedStateHandle)

I don't think this is documented very well. I had to look at the Jetpack source code to learn it.

Since your ViewModel needs a MusicServiceConnection parameter, you cannot use the default factory. You will need to supply an explicit ViewModelProvider.Factory to the by viewModels() as an argument.

This training has an example of creating a ViewModelProvider.Factory class for a ViewModel that needs a special argument for the constructor. In your case, the solution might look like this:

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

I don't know where your MusicServiceConnection class comes from, but let's pretend you can simply instantiate it one time for your Activity with an empty constructor. Then you would replace

private val songViewModel: SongViewModel by viewModels()

with

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

Notice that you are passing a lambda function to viewModels instead of directly passing an instance of your factory. This is because the factory is lazily created only one time, even if your Activity is recreated multiple times (like if the user rotates the screen back and forth). The code inside this lambda is only called once when the app navigates to this screen, even if the Activity is recreated for screen rotations or other configuration changes.

Tenfour04
  • 83,111
  • 11
  • 94
  • 154
  • hey @Tenfour04 , I am still not getting rid of this error maybe I should learn viewModels more thoroughly, before implementing it, BTW thank you for the explanation here is my project [ project ](https://github.com/th3kumar/Snoozz-Sleeping-Buddy) please have look and guide me – Sidharth Aug 12 '22 at 18:03
  • You should be getting a different error (if any error at all) if you switched to `@Inject`. I don't use Hilt, so can't help much with that. Look up how to inject view models when using Hilt. Maybe you're forgetting to add your MusicServiceConnection to your provider. – Tenfour04 Aug 12 '22 at 18:10
  • Thanks, @Tenfour04 you sharpen my concepts about viewmodels – Sidharth Aug 16 '22 at 13:10