View model initialization using "by viewModels()" vs "ViewModelProvider(this).get(ViewModel::class.java)" in android
Asked Answered
P

3

32

We can initialize the ViewModel class using

private val viewModel: CharactersViewModel by viewModels()

or

viewModel = ViewModelProvider(this).get(CharactersViewModel::class.java)

Herer CharactersViewModel is our ViewModel class. My question is when to use which? Do both contain the same purpose? I have read the android official documentation of ViewModel.The documentation says by viewModels() Kotlin property delegate. But unfortunately failed to understand it. Can anyone help me understand this?

Photoactive answered 18/12, 2020 at 5:47 Comment(0)
P
42

Both of them do the same thing, but there is a discriminative advantage for the first one. Kotlin property delegation uses the idea of Lazy Initialization. On Wikipedia you can find a brief definition for it:

In computer programming, lazy initialization is the tactic of delaying the creation of an object, the calculation of a value, or some other expensive process until the first time it is needed. It is a kind of lazy evaluation that refers specifically to the instantiation of objects or other resources.

Therefore, when you use the first approach you mentioned, you take the advantage of lazy properties. It means that the ViewModel instance becomes created only upon first access.

Given below code as an example:

class YourFragment : Fragment() {

    private val viewModel: CharactersViewModel by viewModels()

    // other codes ...

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        
        // doing some view initialization ...

        viewModel.someLiveData.observe(viewLifecycleOwner) {
            // ...
        }
    }
}

If viewModel.someLiveData.observe(viewLifecycleOwner) is the first time that the viewModel field is touched, the instantiation of it will happen there. (creation of a CharactersViewModel instance)

So, using lazy initialization of objects like the view model reduces the start-up impact of your fragment which leads to faster loading and showing its content as opposed to direct initialization of them.

Proglottis answered 18/12, 2020 at 7:12 Comment(4)
It's been HOURS, HOURS! And now I see I had an "=" instead of a ":" causing an error "Classifier 'ViewModel' does not have a companion object, and thus must be initialized here"Phytogenesis
Some people initialize like this can you please explain the difference val blockedViewModel: BlockedViewModel by lazy { BlockedViewModel(ContactRepository(this)) }Translocate
what do i need to import on my gradle file to use viewModels()Gassaway
Perhaps fragment-ktx: developer.android.com/jetpack/androidx/releases/fragmentProglottis
B
9

Those two statements are equal.

The Kotlin keyword by simply saves you some code, saves you the pain of using reflection to access your VM and looks way cleaner, if you as me.

Either or should do the job for ya :)

Bardo answered 18/12, 2020 at 5:55 Comment(0)
H
2

To add to the already mentioned answers,

The lateinit modifier tells the type-checker that a var property will be initialised "by magic" before it is used, and so does not need to be given a nullable type. It prevents the type checker helping you initialise properties correctly. Instead, programming errors in initialisation are reported by exceptions later at runtime.

It is intended for when Kotlin code is interfacing with legacy Java frameworks that use reflection to poke values into fields behind the back of the type checker and visibility modifiers.

We should try to avoid lateinit in our code, if possible, in this case you should use

private val viewModel: CharactersViewModel by viewModels()
Hydrargyrum answered 24/5, 2021 at 7:23 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.