3

I am trying to pass a constructor parameter in the ViewModel class. But I don't understand the best approach to do this. Please take a look at the code and comment and let me know the easiest and appropriate way.

class MainActivity : AppCompatActivity() {
    private lateinit var binding: ActivityMainBinding

    // Question 1: How can I pass constructor parameter if I use this delegate feature?
    // private lateint var viewModel: MainViewModel by viewModels()

    private lateinit var viewModel: MainViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        val repository = Repository()

        // Passing constructor directly works.
        viewModel = MainViewModel(repository)

        // Question 2: Why do I need this factory class / method as it works without these two lines.

//        val viewModelFactory = MainViewModelFactory(repository)
//        viewModel = ViewModelProvider(this, viewModelFactory).get(MainViewModel::class.java)


        viewModel.getPost()
        viewModel.mResponse.observe(this, Observer { response ->
            if (response.isSuccessful) {
                Log.d("Response: ", response.body()?.userId.toString())
                Log.d("Response: ", response.body()?.id.toString())
                binding.textView.text = response.body()?.title.toString()
                Log.d("Response: ", response.body()?.body.toString())
            } else {
                Log.d("Response: ", response.errorBody().toString())
                binding.textView.text = response.code().toString()
            }
        })

    }
}
Code Lover
  • 33
  • 1
  • 5

2 Answers2

3
// Question 1: How can I pass constructor parameter if I use this delegate feature?
// private lateint var viewModel: MainViewModel by viewModels()

You would pass your ViewModelFactory as a parameter to viewModels(). Or, depending on the nature of the parameter, use a dependency inversion framework (Dagger/Hilt, Koin, etc.). In your case, you seem to be passing some sort of repository, in which case a dependency inversion framework is a popular solution.

   // Question 2: Why do I need this factory class / method as it works without these two lines.

Because it does not actually work. In particular, that MainViewModel instance does not survive a configuration change (e.g., screen rotation). If you want to use the Jetpack ViewModel system correctly, the ViewModel system has to instantitate your MainViewModel.

CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
  • private lateint var viewModel: MainViewModel by viewModels() How can I pass repository using this approach without using hilt? Can you please show me an example? – Code Lover Oct 24 '21 at 22:47
  • @CodeLover: "Can you please show me an example?" -- I have plenty of examples of using Koin for dependency inversion. I do not have an example of using `by viewModels()` without using dependency inversion. – CommonsWare Oct 24 '21 at 23:11
2

Here is another thread on this topic Why a viewmodel factory is needed in Android?

A ViewModelFactory is used to create the ViewModel if it requires additional arguments beyond the basic constructor.

The purpose is separation of concerns, you should not be instantiating a ViewModel in the Activity because then the Activity has to know about all the parameters the ViewModel requires. The goal is to keep your view code as dumb as possible, so keeping references to data layer code and business logic in your Activity is not a good idea. Ex: Repository

Additionally a ViewModelProvider allows you to share a single instance of a ViewModel between Activities and Fragments if you want, this can let you maintain state in multiple places without passing data around.

Here's an android develop course about MVVM, it includes a lesson about how to properly use a ViewModelFactory https://developer.android.com/codelabs/kotlin-android-training-view-model#7

Matt
  • 186
  • 10
  • 2
    Second sentence is a bit misleading. A ViewModelFactory is used even if a ViewModel has an empty constructor, though you can use the default ViewModelFactory so you don't have to create your own, by passing no factory argument. – Tenfour04 Oct 09 '21 at 15:53
  • Yeah that's true, in my experience you only need to implement your own if you have additional args – Matt Oct 09 '21 at 16:12