0
  • I am trying to follow MVVM pattern in my Android app but getting error while creating an instance of ViewModel.
  • Error: Cannot create an instance of class DemoViewModel class.

Here is my code:

DemoFragment.kt:

class DemoFragment : Fragment(R.layout.fragment_demo) {

    lateinit var mViewModel: DemoViewModel

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        mViewModel=ViewModelProvider(this).get(DemoViewModel::class.java)
        mViewModel.getSomeData()
    }
}

DemoViewModel.kt:

class DemoViewModel(val demoRepository: DemoRepository) : ViewModel() {

    fun getSomeData() {
        Log.d("DemoViewModel", "${demoRepository.getData()}")
    }
}

DemoRepository.kt:

interface DemoRepository {
    fun getData(): Boolean
}

class DemoImpl : DemoRepository {
    override fun getData() = false
}
Sumit Shukla
  • 4,116
  • 5
  • 38
  • 57

2 Answers2

1

You need to use ViewModelFactory. Because there is "demoRepository" in your primary builder.

    class DemoViewModelFactory constructor(private val repository:DemoImpl): ViewModelProvider.Factory {

     override fun <T : ViewModel> create(modelClass: Class<T>): T {
        return if (modelClass.isAssignableFrom(DemoViewModel::class.java!!)) {
            DemoViewModel(this.repository) as T
        } else {
            throw IllegalArgumentException("ViewModel Not Found")
        }
    }
}

Usage

viewModel = ViewModelProvider(this, DemoViewModelFactory(repositoryObject)).get(DemoViewModel::class.java)
Sam Chen
  • 7,597
  • 2
  • 40
  • 73
Arda Kazancı
  • 8,341
  • 4
  • 28
  • 50
1

I would encourage you to use "by viewModels()" extension function to create viewModel instance easily. Note that you should add following dependency to use it:

implementation 'androidx.fragment:fragment-ktx:1.2.5'

Sample Fragment Implementation:

class DemoFragment : Fragment() {

   // Use the 'by ViewModels()' Kotlin property delegate
   // from the fragment-ktx artifact
   private val model: DemoViewModel by viewModels()

   override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
      super.onViewCreated(view, savedInstanceState)
      model.selected.observe(viewLifecycleOwner, Observer<Item> { item ->
        // Update the UI
      })
   }
}

Then you can inject an instance of your repository via constructor injection by Dagger or Hilt etc.

Baki Kocak
  • 389
  • 1
  • 9
  • So I won't have to use ViewModelFactory when there are parameters in my repository class or not? – Sumit Shukla Jan 03 '21 at 12:01
  • You should inject your repository into your viewModel through its constructor. It might not seem necessary at first but it is a good habit to make your code well-organized and testable. Please find an example below that I just made with HILT: https://github.com/bakikocak/Android-MVVM-Sample/blob/main/app/src/main/java/com/bakikocak/android_mvvm_sample/ui/list/MovieListViewModel.kt – Baki Kocak Jan 03 '21 at 14:33
  • The idea basically to have a the UI (Activity, Fragment) as simple as it can be. So that your fragment should not know anything about the repo. It should only observe the changes in viewModel and make ui changes. – Baki Kocak Jan 03 '21 at 14:37